├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other.md ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── COPYRIGHT ├── Cargo.lock.msrv ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── lib ├── Cargo.toml ├── README.md └── src │ ├── anon.rs │ ├── autoimpl.rs │ ├── autoimpl │ ├── for_deref.rs │ ├── impl_misc.rs │ └── impl_using.rs │ ├── default.rs │ ├── fields.rs │ ├── generics.rs │ ├── lib.rs │ └── scope.rs ├── src └── lib.rs └── tests ├── autoimpl.rs ├── autoimpl_enum.rs ├── for_deref.rs ├── impl_anon.rs ├── impl_default.rs ├── impl_scope.rs └── newtype.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: dhardy 4 | liberapay: dhardy 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Summary** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | ```rust 15 | // Sample code 16 | ``` 17 | 18 | **Expansion** 19 | ```rust 20 | // Result of 'cargo expand', if available 21 | // To install: 22 | // cargo install cargo-expand 23 | ``` 24 | 25 | **Additional information** 26 | Expected result, suggested error message or other details 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Summary** 11 | Concise description of problem 12 | 13 | **Sample code** 14 | ```rust 15 | // Sample code showing context and suggested macro syntax 16 | ``` 17 | 18 | **Expansion** 19 | ```rust 20 | // Result of macro expansion for the above sample. 21 | ``` 22 | 23 | **Additional context** 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Blank issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ master, '[0-9]+.[0-9]+' ] 6 | pull_request: 7 | branches: [ master, '[0-9]+.[0-9]+' ] 8 | 9 | jobs: 10 | nightly: 11 | name: Nightly, format and Doc 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Install toolchain 16 | uses: dtolnay/rust-toolchain@nightly 17 | - name: Build docs 18 | run: cargo doc --all-features --all --no-deps 19 | - name: Test impl-tools-lib 20 | run: cargo test --manifest-path lib/Cargo.toml --all-features 21 | - name: Test impl-tools 22 | run: cargo test --all-features 23 | 24 | beta: 25 | name: Beta on MacOS 26 | runs-on: macos-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Install toolchain 31 | uses: dtolnay/rust-toolchain@beta 32 | - name: Test impl-tools-lib 33 | run: cargo test --manifest-path lib/Cargo.toml --all-features 34 | - name: Test impl-tools 35 | run: cargo test --all-features 36 | 37 | stable: 38 | name: Stable on Windows 39 | runs-on: windows-latest 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | - name: Install toolchain 44 | uses: dtolnay/rust-toolchain@stable 45 | - name: Test impl-tools-lib 46 | run: cargo test --manifest-path lib/Cargo.toml --all-features 47 | - name: Test impl-tools 48 | run: cargo test --all-features 49 | 50 | msrv: 51 | name: MSRV 52 | runs-on: macos-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v4 56 | - name: Install toolchain 57 | uses: dtolnay/rust-toolchain@1.61.0 58 | - name: Use Cargo.lock.msrv 59 | run: cp Cargo.lock.msrv Cargo.lock 60 | - name: Test impl-tools-lib 61 | run: cargo test --manifest-path lib/Cargo.toml --all-features --lib --tests 62 | - name: Test impl-tools 63 | run: cargo test --all-features --lib --tests 64 | 65 | workspace: 66 | runs-on: ubuntu-latest 67 | steps: 68 | - uses: actions/checkout@v4 69 | - uses: dtolnay/rust-toolchain@master 70 | with: 71 | toolchain: stable 72 | components: rustfmt 73 | - name: rustfmt 74 | run: cargo fmt --all -- --check 75 | - name: clippy 76 | run: cargo clippy --workspace -- -D warnings 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 3 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## [0.10.3], [impl-tools-lib-0.11.1] — 2024-12-21 6 | 7 | - Let `#[autoimpl]` on traits support function arguments using `mut` and destructuring patterns (#45) 8 | - Improve documentation for `#[autoimpl]` (#46) 9 | 10 | ## [0.10.2], [impl-tools-lib-0.11.0] — 2024-12-09 11 | 12 | Re-release, bumping `impl-tools-lib` to v0.11.0 since it turns out that switching to `proc-macro-error2` was an API-breaking release for `impl-tools-lib` (this is only apparent if a macro emits an error, resulting in a message like "help: message: proc-macro-error2 API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error] 13 | "). 14 | 15 | `impl-tools` is unaffected excepting where an incompatible version of `impl-tools-lib` is used. Broken versions will be yanked. 16 | 17 | ## [0.10.1] — 2024-10-21 18 | 19 | - Improve CI workflows (#38) 20 | - Explicit MSRV = 1.58 (#38) 21 | - Replace dependency `proc-macro-error` with `proc-macro-error2` (#41) 22 | - Bump MSRV to 1.61 (#42) 23 | 24 | ## [0.10.0] — 2023-09-07 25 | 26 | - Rename `singleton!` → `impl_anon!` (#36) 27 | - Reorganise `impl-tools-lib`: new `anon` and `scope` public modules (#36) 28 | 29 | ## [0.9.1] — 2023-09-07 30 | 31 | - Fix clone for fields which auto-deref (issue #34) 32 | 33 | ## [0.9.0] — 2023-06-28 34 | 35 | - Update to syn v2.0.0 36 | 37 | ## [0.8.0] — 2023-02-07 38 | 39 | - Bump MSRV to 1.58.0 (#31) 40 | - `#[autoimpl(Clone, Debug, PartialEq, Eq, Hash)]` now all support enums 41 | (with optional `where` clause, without `ignore` clauses) (#31) 42 | - Add `impl_tools_lib::ImplTrait::enum_impl`, `enum_items` with default impls; 43 | `ImplTraits::expand` now supports enums (#31) 44 | - Add `impl_tools_lib::Ident_formatter` utility (#31) 45 | 46 | Note: `PartialOrd, Ord` *could* now support enums (unimplemented). `ignore` and 47 | `using` clauses are deliberately not supported (due to syntactic ambiguity). 48 | 49 | ## [0.6.2], `impl-tools-lib` [0.7.1] — 2022-12-16 50 | 51 | - Fix `#[autoimpl]` on traits: copy `#[cfg(..)]` attributes (#30) 52 | 53 | ## [0.6.1], `impl-tools-lib` [0.7.0] — 2022-12-01 54 | 55 | - Better diagnostics for trait-redefinition: require `Deref` bound (#28) 56 | - Document `Deref` with custom `Target` type 57 | 58 | `impl-tools-lib` has breaking changes and therefore a higher version number: 59 | 60 | - Replace free function `impl_generics` with method `Generics::impl_generics` 61 | - Add method `Generics::ty_generics` 62 | 63 | Note: next breaking release for `impl-tools` should bump version to match `-lib`. 64 | 65 | ## [0.6.0] — 2022-11-17 66 | 67 | - Add `ImplTrait::support_path_args`, `ImplArgs::path_args` (#26) 68 | - Path args: support `Deref` (#26) 69 | 70 | ## [0.5.2] — 2022-10-06 71 | 72 | - Add `singleton!` macro (#25) 73 | 74 | ## [0.5.1] — 2022-09-23 75 | 76 | - Fix: do not copy attributes on trait items (#24) 77 | 78 | ## [0.5.0] — 2022-09-22 79 | 80 | - `#[autoimpl]` on traits now merges trait generics with macro generics (#21) 81 | - `lib::autoimpl::struct_items` returns the trait path in addition to impl items (#22) 82 | - Add `lib::autoimpl::ImplArgs::for_fields`, `for_fields_iter` (#22) 83 | - Add autoimpl support for `Copy`, `AsRef`, `AsMut`, `Borrow`, `BorrowMut`, 84 | `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash` (#22) 85 | - Add `#[automatically_derived]` annotation to generated impls (#22) 86 | 87 | ## [0.4.4] — 2022-09-19 88 | 89 | - Fix `#[autoimpl]` on traits for items with where bounds on `Self` (#20) 90 | 91 | ## [0.4.3] — 2022-09-17 92 | 93 | - Fix `#[autoimpl]` on traits for GATs with where clauses (#19) 94 | 95 | ## [0.4.2] — 2022-09-17 96 | 97 | - Correct release of 0.4.1 (which bumped the version of impl-tools without 98 | bumping impl-tools-lib) (#18) 99 | - Fix `#[autoimpl]` on traits for GATs and attributes on trait const/method/type items (#17) 100 | 101 | ## [0.4.1] — 2022-09-17 102 | 103 | No changes (prefer 0.4.2 instead). 104 | 105 | ## [0.4.0] — 2022-08-19 106 | 107 | Change signature of `ScopeAttr::apply`: replace `args: TokenStream, attr_span: Span` 108 | with `attr: Attribute` (#15). 109 | 110 | ## [0.3.2] — 2022-06-01 111 | 112 | Support `no_std`. Support matching standard traits via paths from `core`/`alloc` 113 | as well as via paths from `std`. 114 | 115 | ## [0.3.1] — 2022-04-17 116 | 117 | Documentation improvements only. 118 | 119 | ## [0.3.0] — 2022-03-29 120 | 121 | The library now supports extensibility. Most code has been moved to a new crate, 122 | `impl-tools-lib`. Users may replace `impl-tools` with their own front-end 123 | proc-macro crate, adding/removing the traits supported by `#[autoimpl]` and the 124 | attributes supported by `impl_scope!`. 125 | 126 | - Extensibility for `impl_scope!` 127 | - Extensibility for `#[autoimpl]` 128 | - Permit path arguments in `#[autoimpl]` 129 | - Bug-fix for `#[autoimpl(Debug)]` on tuple and unit structs 130 | - Lots of internal code revision 131 | 132 | ## [0.2.0] — 2022-03-23 133 | 134 | Add `impl_scope!` function-like macro (derived from `kas_macros::widget!`) and 135 | `#[impl_default]` attribute macro. 136 | 137 | ## [0.1.0] — 2022-03-21 138 | 139 | New release, including the `#[autoimpl]` attribute macro (extracted from 140 | `kas-macros` crate). 141 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | This work is copyrighted by the following contributors: 2 | 3 | Diggory Hardy 4 | 5 | This list may be incomplete. 6 | -------------------------------------------------------------------------------- /Cargo.lock.msrv: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 16 | 17 | [[package]] 18 | name = "doc-comment" 19 | version = "0.3.3" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 22 | 23 | [[package]] 24 | name = "getrandom" 25 | version = "0.2.10" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 28 | dependencies = [ 29 | "cfg-if", 30 | "libc", 31 | "wasi", 32 | ] 33 | 34 | [[package]] 35 | name = "impl-tools" 36 | version = "0.10.3" 37 | dependencies = [ 38 | "autocfg", 39 | "doc-comment", 40 | "impl-tools-lib", 41 | "proc-macro-error2", 42 | "syn", 43 | "twox-hash", 44 | ] 45 | 46 | [[package]] 47 | name = "impl-tools-lib" 48 | version = "0.11.1" 49 | dependencies = [ 50 | "proc-macro-error2", 51 | "proc-macro2", 52 | "quote", 53 | "syn", 54 | ] 55 | 56 | [[package]] 57 | name = "libc" 58 | version = "0.2.147" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 61 | 62 | [[package]] 63 | name = "ppv-lite86" 64 | version = "0.2.17" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 67 | 68 | [[package]] 69 | name = "proc-macro-error-attr2" 70 | version = "2.0.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 73 | dependencies = [ 74 | "proc-macro2", 75 | "quote", 76 | ] 77 | 78 | [[package]] 79 | name = "proc-macro-error2" 80 | version = "2.0.1" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 83 | dependencies = [ 84 | "proc-macro-error-attr2", 85 | "proc-macro2", 86 | "quote", 87 | ] 88 | 89 | [[package]] 90 | name = "proc-macro2" 91 | version = "1.0.85" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" 94 | dependencies = [ 95 | "unicode-ident", 96 | ] 97 | 98 | [[package]] 99 | name = "quote" 100 | version = "1.0.36" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 103 | dependencies = [ 104 | "proc-macro2", 105 | ] 106 | 107 | [[package]] 108 | name = "rand" 109 | version = "0.8.5" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 112 | dependencies = [ 113 | "libc", 114 | "rand_chacha", 115 | "rand_core", 116 | ] 117 | 118 | [[package]] 119 | name = "rand_chacha" 120 | version = "0.3.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 123 | dependencies = [ 124 | "ppv-lite86", 125 | "rand_core", 126 | ] 127 | 128 | [[package]] 129 | name = "rand_core" 130 | version = "0.6.4" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 133 | dependencies = [ 134 | "getrandom", 135 | ] 136 | 137 | [[package]] 138 | name = "static_assertions" 139 | version = "1.1.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 142 | 143 | [[package]] 144 | name = "syn" 145 | version = "2.0.66" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 148 | dependencies = [ 149 | "proc-macro2", 150 | "quote", 151 | "unicode-ident", 152 | ] 153 | 154 | [[package]] 155 | name = "twox-hash" 156 | version = "1.6.3" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 159 | dependencies = [ 160 | "cfg-if", 161 | "rand", 162 | "static_assertions", 163 | ] 164 | 165 | [[package]] 166 | name = "unicode-ident" 167 | version = "1.0.11" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 170 | 171 | [[package]] 172 | name = "wasi" 173 | version = "0.11.0+wasi-snapshot-preview1" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 176 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "impl-tools" 3 | version = "0.10.3" 4 | authors = ["Diggory Hardy "] 5 | edition = "2021" 6 | license = "MIT/Apache-2.0" 7 | description = "Helper macros: autoimpl" 8 | keywords = ["proc-macro", "macro", "derive", "trait", "procedural"] 9 | categories = ["development-tools::procedural-macro-helpers"] 10 | repository = "https://github.com/kas-gui/impl-tools" 11 | readme = "README.md" 12 | documentation = "https://docs.rs/impl-tools/" 13 | rust-version = "1.61.0" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies.proc-macro-error2] 19 | version = "2.0" 20 | default-features = false 21 | 22 | [dependencies.syn] 23 | version = "2.0.0" 24 | 25 | [dependencies.impl-tools-lib] 26 | version = "0.11.1" 27 | path = "lib" 28 | 29 | [dev-dependencies] 30 | doc-comment = "0.3.3" 31 | twox-hash = "1.6.3" 32 | 33 | [build-dependencies] 34 | autocfg = "1.1.0" 35 | 36 | [workspace] 37 | members = ["lib"] 38 | 39 | [lints.rust] 40 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(rustc_1_65)', 'cfg(mock_feature)'] } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Impl-tools 2 | ======= 3 | 4 | [![Test Status](https://github.com/kas-gui/impl-tools/workflows/Tests/badge.svg?event=push)](https://github.com/kas-gui/impl-tools/actions) 5 | [![Latest version](https://img.shields.io/crates/v/impl-tools.svg)](https://crates.io/crates/impl-tools) 6 | [![API](https://docs.rs/impl-tools/badge.svg)](https://docs.rs/impl-tools) 7 | [![Minimum rustc version](https://img.shields.io/badge/rustc-1.61+-lightgray.svg)](https://github.com/kas-gui/impl-tools#supported-rust-versions) 8 | 9 | A set of helper macros 10 | 11 | 12 | Macros 13 | ------ 14 | 15 | ### Autoimpl 16 | 17 | `#[autoimpl]` is a partial replacement for `#[derive]`, supporting: 18 | 19 | - Explicit `where` clause on generic parameters 20 | - No implicit bounds on generic parameters beyond those required by the type 21 | - Traits like `Deref` by `using` a named field 22 | - Traits like `Debug` may `ignore` named fields 23 | 24 | `#[autoimpl]` may also be used on trait definitions to impl for specified types 25 | supporting `Deref`. 26 | 27 | Unlike [alternatives](#alternatives), `#[autoimpl]` has minimal and intuitive syntax. 28 | 29 | ```rust 30 | use impl_tools::autoimpl; 31 | use std::fmt::Debug; 32 | 33 | // Impl Animal for Box where T: Animal + ?Sized 34 | #[autoimpl(for Box)] 35 | trait Animal { 36 | fn number_of_legs(&self) -> u32; 37 | } 38 | 39 | // Impl Debug for Named omitting field animal from output 40 | #[autoimpl(Debug ignore self.animal where T: Debug)] 41 | // Impl Deref and DerefMut to field animal for Named 42 | #[autoimpl(Deref, DerefMut using self.animal)] 43 | struct Named { 44 | name: T, 45 | animal: A, 46 | } 47 | 48 | fn main() { 49 | struct Fish; 50 | impl Animal for Fish { 51 | fn number_of_legs(&self) -> u32 { 52 | 0 53 | } 54 | } 55 | 56 | let my_fish = Named { 57 | name: "Nemo", 58 | animal: Box::new(Fish), 59 | }; 60 | 61 | assert_eq!( 62 | format!("{my_fish:?} has {} legs!", my_fish.number_of_legs()), 63 | r#"Named { name: "Nemo", .. } has 0 legs!"# 64 | ); 65 | } 66 | ``` 67 | 68 | #### New-type wrappers 69 | 70 | A combination of `Deref` on the new-type and trait-reimplementation on the 71 | trait allows succinct new-type patterns: 72 | 73 | ```rust 74 | use impl_tools::autoimpl; 75 | use std::sync::Arc; 76 | 77 | // Impl Foo for &T, &mut T and Arc 78 | #[autoimpl(for &T, &mut T, Arc)] 79 | // Optional: impl Foo for NewFoo (requires NewFoo: Deref) 80 | #[autoimpl(for NewFoo)] 81 | pub trait Foo { 82 | fn success(&self) -> bool; 83 | } 84 | 85 | // Impl Deref and DerefMut to a Target which itself supports Foo 86 | #[autoimpl(Deref, DerefMut using self.0)] 87 | pub struct NewFoo(T); 88 | 89 | // Impl Deref and DerefMut to a Target which itself supports Foo 90 | #[autoimpl(Deref, DerefMut using self.0)] 91 | pub struct ArcDynFoo(Arc); 92 | 93 | #[test] 94 | fn test_foo_newtypes() { 95 | struct Success; 96 | impl Foo for Success { 97 | fn success(&self) -> bool { true } 98 | } 99 | 100 | // We can now directly call Foo's methods on the wrapper: 101 | assert!(NewFoo(Success).success()); 102 | assert!(ArcDynFoo(Arc::new(Success)).success()); 103 | } 104 | ``` 105 | 106 | See [`tests/newtype.rs`](https://github.com/kas-gui/impl-tools/blob/master/tests/newtype.rs) for more variants of this pattern. 107 | 108 | 109 | ### Impl Default 110 | 111 | `#[impl_default]` implements `std::default::Default`: 112 | 113 | ```rust 114 | #[impl_tools::impl_default(Tree::Ash)] 115 | enum Tree { Ash, Beech, Birch, Willow } 116 | 117 | impl_tools::impl_scope! { 118 | #[impl_default] 119 | struct Copse { 120 | tree_type: Tree, 121 | number: u32 = 7, 122 | } 123 | } 124 | ``` 125 | 126 | Note: `#[impl_default]` is matched within an `impl_scope!` regardless of imports. 127 | 128 | ### Impl Scope 129 | 130 | `impl_scope!` is a function-like macro used to define a type plus its 131 | implementations. It supports `impl Self` syntax: 132 | 133 | ```rust 134 | use std::fmt::Display; 135 | 136 | impl_tools::impl_scope! { 137 | /// I don't know why this exists 138 | pub struct NamedThing { 139 | name: T, 140 | func: F, 141 | } 142 | 143 | // Repeats generic parameters of type 144 | impl Self { 145 | fn format_name(&self) -> String { 146 | format!("{}", self.name) 147 | } 148 | } 149 | 150 | // Merges generic parameters of type 151 | impl Self where F: Fn(&str) -> O { 152 | fn invoke(&self) -> O { 153 | (self.func)(&self.format_name()) 154 | } 155 | } 156 | } 157 | ``` 158 | 159 | Caveat: `rustfmt` won't currently touch the contents. Hopefully that 160 | [can be fixed](https://github.com/rust-lang/rustfmt/pull/5538)! 161 | 162 | ### Impl Anon 163 | 164 | `impl_anon!` is a function-like macro to construct a single-use struct with 165 | custom implementations (similar: [RFC#2604](https://github.com/rust-lang/rfcs/pull/2604)). 166 | 167 | Example: 168 | ```rust 169 | use std::fmt; 170 | fn main() { 171 | let world = "world"; 172 | let says_hello_world = impl_tools::impl_anon! { 173 | struct(&'static str = world); 174 | impl fmt::Display for Self { 175 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 176 | write!(f, "hello {}", self.0) 177 | } 178 | } 179 | }; 180 | assert_eq!(format!("{}", says_hello_world), "hello world"); 181 | } 182 | ``` 183 | 184 | 185 | Extensibility 186 | ------------- 187 | 188 | Rust's `#[derive]` macro is extensible via `#[proc_macro_derive]` in a `proc-macro` crate. 189 | Our macros cannot be extended in the same way, but they can be extended via a new front-end: 190 | 191 | 1. Create a copy of the `impl-tools` crate to create a new "front-end" (`proc-macro` crate). 192 | This crate is contains only a little code over the [`impl-tools-lib`] crate. 193 | 2. To extend `#[autoimpl]`, write an impl of [`ImplTrait`] and add it to the attribute's definition. 194 | To extend `impl_scope!`, write an impl of [`ScopeAttr`] and add it to the macro's definition. 195 | 3. Depend on your new front end crate instead of `impl-tools`. 196 | 197 | For an example of this approach, see [kas-macros](https://github.com/kas-gui/kas/tree/master/crates/kas-macros). 198 | 199 | [`impl-tools-lib`]: https://docs.rs/impl-tools-lib/ 200 | [`ImplTrait`]: https://docs.rs/impl-tools-lib/latest/impl_tools_lib/autoimpl/trait.ImplTrait.html 201 | [`ScopeAttr`]: https://docs.rs/impl-tools-lib/latest/impl_tools_lib/trait.ScopeAttr.html 202 | 203 | 204 | Supported Rust Versions 205 | ------------------------------ 206 | 207 | The MSRV is 1.61.0. 208 | 209 | When using a sufficiently recent compiler version (presumably 1.65.0), generic associated types 210 | are supported (only applicable to `#[autoimpl]` on trait definitions using GATs). 211 | 212 | 213 | Alternatives 214 | ------------ 215 | 216 | ### Derive alternatives 217 | 218 | Both [Educe](https://crates.io/crates/educe) and [Derivative](https://crates.io/crates/derivative) 219 | have similar functionality: the ability to implement standard traits with more flexibility than 220 | libstd's `#[derive]`. 221 | 222 | In comparison, impl-tools' `#[autoimpl]` has cleaner syntax but is less flexible: 223 | ```rust,ignore 224 | #[derive(Derivative)] 225 | #[derivative(PartialEq, Eq)] 226 | struct Foo { 227 | foo: S, 228 | #[derivative(PartialEq="ignore")] 229 | bar: u8, 230 | #[derivative(PartialEq(bound=""), Eq(bound=""))] 231 | ptr: *const T, 232 | } 233 | 234 | #[derive(Educe)] 235 | #[educe(PartialEq(bound = "S: PartialEq"), Eq(bound = "S: Eq"))] 236 | struct Foo { 237 | foo: S, 238 | #[educe(PartialEq(ignore))] 239 | bar: u8, 240 | ptr: *const T, 241 | } 242 | 243 | // impl-tools: 244 | #[autoimpl(PartialEq, Eq ignore self.bar where S: trait)] 245 | struct Foo { 246 | foo: S, 247 | bar: u8, 248 | ptr: *const T, 249 | } 250 | ``` 251 | 252 | Note: `#[derive]` and `Derivative` add bounds like `S: PartialEq, T: PartialEq` on generic parameters by default; `Educe` and `impl-tools` do not. 253 | 254 | ### Derive extensions 255 | 256 | [derive_more](https://crates.io/crates/derive_more) isn't exactly an "alternative", simply 257 | supporting `#[derive]` for more standard traits such as `Add` and `From`. 258 | This is not (currently) supported by `#[autoimpl]` (or, to my knowledge, any alternative). 259 | 260 | [auto_impl](https://crates.io/crates/auto_impl/) allows implementing a trait for reference types 261 | (`&`, `&mut`, `Box`, `Rc`, `Arc`) as well as function types. The former (reference types) is 262 | supported by `#[autoimpl]` (and is slightly more general): 263 | ```rust,ignore 264 | // auto_impl: 265 | #[auto_impl(&, Box)] 266 | trait Foo { 267 | fn foo(&self); 268 | } 269 | 270 | // impl-tools: 271 | #[autoimpl(for &T, Box)] 272 | trait Foo { 273 | fn foo(&self); 274 | } 275 | ``` 276 | 277 | 278 | Copyright and Licence 279 | --------------------- 280 | 281 | The [COPYRIGHT](COPYRIGHT) file includes a list of contributors who claim 282 | copyright on this project. This list may be incomplete; new contributors may 283 | optionally add themselves to this list. 284 | 285 | The impl-tools library is published under the terms of the Apache License, Version 2.0. 286 | You may obtain a copy of this licence from the [LICENSE](LICENSE) file or on 287 | the following webpage: 288 | 289 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate autocfg; 2 | 3 | fn main() { 4 | let ac = autocfg::new(); 5 | ac.emit_rustc_version(1, 65); 6 | 7 | autocfg::rerun_path("build.rs"); 8 | } 9 | -------------------------------------------------------------------------------- /lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "impl-tools-lib" 3 | version = "0.11.1" 4 | authors = ["Diggory Hardy "] 5 | edition = "2021" 6 | license = "MIT/Apache-2.0" 7 | description = "Helper macros: autoimpl" 8 | keywords = ["derive", "trait"] 9 | repository = "https://github.com/kas-gui/impl-tools" 10 | readme = "README.md" 11 | documentation = "https://docs.rs/impl-tools-lib/" 12 | rust-version = "1.61.0" 13 | 14 | [dependencies] 15 | quote = "1.0" 16 | proc-macro2 = "1.0" 17 | 18 | [dependencies.proc-macro-error2] 19 | version = "2.0" 20 | default-features = false 21 | 22 | [dependencies.syn] 23 | version = "2.0.0" 24 | # We need 'extra-traits' for equality testing 25 | # We need 'full' for parsing macros within macro arguments 26 | features = ["extra-traits", "full", "visit", "visit-mut"] 27 | 28 | [lints.clippy] 29 | needless_lifetimes = "allow" 30 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | Impl-tools library 2 | ======= 3 | 4 | This is the library behind the [impl-tools] crate. 5 | 6 | This library may be used directly to write custom variants of the [impl-tools] 7 | macros, for example extending `#[autoimpl]` to support other traits or 8 | writing new attribute macros to be evaluated within a `impl_scope!`. 9 | 10 | [impl-tools]: https://crates.io/crates/impl-tools 11 | 12 | 13 | Copyright and Licence 14 | --------------------- 15 | 16 | The [COPYRIGHT](COPYRIGHT) file includes a list of contributors who claim 17 | copyright on this project. This list may be incomplete; new contributors may 18 | optionally add themselves to this list. 19 | 20 | The impl-tools library is published under the terms of the Apache License, Version 2.0. 21 | You may obtain a copy of this licence from the [LICENSE](LICENSE) file or on 22 | the following webpage: 23 | 24 | -------------------------------------------------------------------------------- /lib/src/anon.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! The `impl_anon!` macro 7 | 8 | use crate::fields::{Field, Fields, FieldsNamed, FieldsUnnamed, StructStyle}; 9 | use crate::scope::{Scope, ScopeItem}; 10 | use crate::IdentFormatter; 11 | use proc_macro2::{Span, TokenStream}; 12 | use quote::{quote, ToTokens, TokenStreamExt}; 13 | use syn::token::{Brace, Colon, Comma, Eq, Paren, Semi}; 14 | use syn::{parse_quote, punctuated::Punctuated, spanned::Spanned}; 15 | use syn::{Attribute, GenericParam, Generics, Ident, ItemImpl, Member, Token, Type, TypePath}; 16 | 17 | /// A field of a [`Anon`] 18 | #[derive(Debug)] 19 | pub struct AnonField { 20 | /// Field attributes 21 | pub attrs: Vec, 22 | /// Field visibility 23 | pub vis: syn::Visibility, 24 | /// Field identifier (regular structs only, and optional even there) 25 | pub ident: Option, 26 | /// Separator between identifier and type 27 | /// 28 | /// This is present only given an explicit identifier *and* an explicit type. 29 | pub colon_token: Option, 30 | /// Field type 31 | /// 32 | /// In case the type is omitted, this has value [`Type::Infer`]. 33 | pub ty: Type, 34 | /// Optional non-default value assignment 35 | pub assignment: Option<(Eq, syn::Expr)>, 36 | } 37 | 38 | /// Contents of `impl_anon!` 39 | /// 40 | /// The impl_anon macro may be used to conveniently declare a struct's type, 41 | /// implementations and construct an instance. 42 | /// This struct represents the macro's input. 43 | #[derive(Debug)] 44 | pub struct Anon { 45 | /// Struct attributes 46 | pub attrs: Vec, 47 | /// `struct` token 48 | pub token: Token![struct], 49 | /// (Explicit) struct generics 50 | /// 51 | /// Note: the macro instantiated type may have extra generics. 52 | pub generics: Generics, 53 | /// Struct style: unit/tuple/regular 54 | pub style: StructStyle, 55 | /// Struct fields 56 | pub fields: Punctuated, 57 | /// (Explicit) struct implementations 58 | pub impls: Vec, 59 | } 60 | 61 | impl Anon { 62 | /// Convert to a [`AnonScope`] 63 | pub fn into_scope(mut self) -> AnonScope { 64 | let mut idfmt = IdentFormatter::new(); 65 | 66 | let mut fields = Punctuated::::new(); 67 | let mut field_val_toks = quote! {}; 68 | 69 | for (index, pair) in self.fields.into_pairs().enumerate() { 70 | let (field, opt_comma) = pair.into_tuple(); 71 | 72 | let mut ident = field.ident.clone(); 73 | let ty = &field.ty; 74 | let field_span = match field.assignment { 75 | None => quote! { #ty }.span(), 76 | Some((ref eq, ref expr)) => quote! { #ty #eq #expr }.span(), 77 | }; 78 | let mem = match self.style { 79 | StructStyle::Regular(_) => { 80 | let id = ident 81 | .unwrap_or_else(|| idfmt.make(format_args!("_field{}", index), field_span)); 82 | ident = Some(id.clone()); 83 | Member::Named(id) 84 | } 85 | StructStyle::Tuple(_, _) => Member::Unnamed(syn::Index { 86 | index: index as u32, 87 | span: field_span, 88 | }), 89 | _ => unreachable!(), 90 | }; 91 | let ty_name = match ident { 92 | None => format!("_Field{}", index), 93 | Some(ref id) => { 94 | let ident = id.to_string(); 95 | let mut buf = "_Field".to_string(); 96 | buf.reserve(ident.len()); 97 | let mut next_upper = true; 98 | for c in ident.chars() { 99 | if c == '_' { 100 | next_upper = true; 101 | continue; 102 | } 103 | 104 | if next_upper { 105 | buf.extend(c.to_uppercase()); 106 | next_upper = false; 107 | } else { 108 | buf.push(c); 109 | } 110 | } 111 | buf 112 | } 113 | }; 114 | 115 | let ty: Type = match field.ty { 116 | Type::ImplTrait(syn::TypeImplTrait { impl_token, bounds }) => { 117 | let span = quote! { #impl_token #bounds }.span(); 118 | let ty = Ident::new(&ty_name, span); 119 | 120 | self.generics.params.push(parse_quote! { #ty: #bounds }); 121 | 122 | Type::Path(TypePath { 123 | qself: None, 124 | path: ty.into(), 125 | }) 126 | } 127 | Type::Infer(infer_token) => { 128 | let ty = Ident::new(&ty_name, infer_token.span()); 129 | self.generics.params.push(parse_quote! { #ty }); 130 | 131 | Type::Path(TypePath { 132 | qself: None, 133 | path: ty.into(), 134 | }) 135 | } 136 | mut ty => { 137 | struct ReplaceInfers<'a> { 138 | index: usize, 139 | params: Vec, 140 | idfmt: &'a mut IdentFormatter, 141 | ty_name: &'a str, 142 | } 143 | let mut replacer = ReplaceInfers { 144 | index: 0, 145 | params: vec![], 146 | idfmt: &mut idfmt, 147 | ty_name: &ty_name, 148 | }; 149 | 150 | impl<'a> syn::visit_mut::VisitMut for ReplaceInfers<'a> { 151 | fn visit_type_mut(&mut self, node: &mut Type) { 152 | let (span, bounds) = match node { 153 | Type::ImplTrait(syn::TypeImplTrait { impl_token, bounds }) => { 154 | (impl_token.span, std::mem::take(bounds)) 155 | } 156 | Type::Infer(infer) => (infer.span(), Punctuated::new()), 157 | _ => return, 158 | }; 159 | 160 | let ident = self 161 | .idfmt 162 | .make(format_args!("{}{}", self.ty_name, self.index), span); 163 | self.index += 1; 164 | 165 | self.params.push(GenericParam::Type(syn::TypeParam { 166 | attrs: vec![], 167 | ident: ident.clone(), 168 | colon_token: Some(Default::default()), 169 | bounds, 170 | eq_token: None, 171 | default: None, 172 | })); 173 | 174 | *node = Type::Path(TypePath { 175 | qself: None, 176 | path: ident.into(), 177 | }); 178 | } 179 | } 180 | syn::visit_mut::visit_type_mut(&mut replacer, &mut ty); 181 | 182 | self.generics.params.extend(replacer.params); 183 | ty 184 | } 185 | }; 186 | 187 | if let Some((_, ref value)) = field.assignment { 188 | field_val_toks.append_all(quote! { #mem: #value, }); 189 | } else { 190 | field_val_toks.append_all(quote! { #mem: Default::default(), }); 191 | } 192 | 193 | fields.push_value(Field { 194 | attrs: field.attrs, 195 | vis: field.vis, 196 | ident, 197 | colon_token: field.colon_token.or_else(|| Some(Default::default())), 198 | ty, 199 | assign: None, 200 | }); 201 | if let Some(comma) = opt_comma { 202 | fields.push_punct(comma); 203 | } 204 | } 205 | 206 | let (fields, semi) = match self.style { 207 | StructStyle::Unit(semi) => (Fields::Unit, Some(semi)), 208 | StructStyle::Regular(brace_token) => ( 209 | Fields::Named(FieldsNamed { 210 | brace_token, 211 | fields, 212 | }), 213 | None, 214 | ), 215 | StructStyle::Tuple(paren_token, semi) => ( 216 | Fields::Unnamed(FieldsUnnamed { 217 | paren_token, 218 | fields, 219 | }), 220 | Some(semi), 221 | ), 222 | }; 223 | 224 | let scope = Scope { 225 | attrs: self.attrs, 226 | vis: syn::Visibility::Inherited, 227 | ident: parse_quote! { _Anon }, 228 | generics: self.generics, 229 | item: ScopeItem::Struct { 230 | token: self.token, 231 | fields, 232 | }, 233 | semi, 234 | impls: self.impls, 235 | generated: vec![], 236 | }; 237 | 238 | AnonScope(scope, field_val_toks) 239 | } 240 | } 241 | 242 | /// A [`Scope`] plus field values 243 | /// 244 | /// This supports dereference to a [`Scope`], allowing macro expansion via 245 | /// [`Scope::apply_attrs`] and other manipulation. 246 | /// 247 | /// Tokens may be generated by [`Self::expand`]. 248 | #[derive(Debug)] 249 | pub struct AnonScope(Scope, TokenStream); 250 | 251 | impl std::ops::Deref for AnonScope { 252 | type Target = Scope; 253 | fn deref(&self) -> &Scope { 254 | &self.0 255 | } 256 | } 257 | impl std::ops::DerefMut for AnonScope { 258 | fn deref_mut(&mut self) -> &mut Scope { 259 | &mut self.0 260 | } 261 | } 262 | 263 | impl AnonScope { 264 | /// Generate the [`TokenStream`] 265 | /// 266 | /// This is a convenience function. It is valid to, instead, (1) call 267 | /// [`Scope::expand_impl_self`] on self, then (2) use the [`ToTokens`] 268 | /// impl on `Scope`. 269 | pub fn expand(mut self) -> TokenStream { 270 | self.expand_impl_self(); 271 | self.into_token_stream() 272 | } 273 | } 274 | 275 | impl ToTokens for AnonScope { 276 | fn to_tokens(&self, tokens: &mut TokenStream) { 277 | let scope = &self.0; 278 | let field_val_toks = &self.1; 279 | 280 | tokens.append_all(quote! { 281 | { 282 | #scope 283 | 284 | _Anon { 285 | #field_val_toks 286 | } 287 | } 288 | }); 289 | } 290 | } 291 | 292 | mod parsing { 293 | use super::*; 294 | use proc_macro_error2::abort; 295 | use syn::parse::{Error, Parse, ParseStream, Result}; 296 | use syn::{braced, parenthesized}; 297 | 298 | fn parse_impl(in_ident: Option<&Ident>, input: ParseStream) -> Result { 299 | let mut attrs = input.call(Attribute::parse_outer)?; 300 | let defaultness: Option = input.parse()?; 301 | let unsafety: Option = input.parse()?; 302 | let impl_token: Token![impl] = input.parse()?; 303 | 304 | let has_generics = input.peek(Token![<]) 305 | && (input.peek2(Token![>]) 306 | || input.peek2(Token![#]) 307 | || (input.peek2(Ident) || input.peek2(syn::Lifetime)) 308 | && (input.peek3(Token![:]) 309 | || input.peek3(Token![,]) 310 | || input.peek3(Token![>]) 311 | || input.peek3(Token![=])) 312 | || input.peek2(Token![const])); 313 | let mut generics: Generics = if has_generics { 314 | input.parse()? 315 | } else { 316 | Generics::default() 317 | }; 318 | 319 | let mut first_ty: Type = input.parse()?; 320 | let self_ty: Type; 321 | let trait_; 322 | 323 | let is_impl_for = input.peek(Token![for]); 324 | if is_impl_for { 325 | let for_token: Token![for] = input.parse()?; 326 | let mut first_ty_ref = &first_ty; 327 | while let Type::Group(ty) = first_ty_ref { 328 | first_ty_ref = &ty.elem; 329 | } 330 | if let Type::Path(_) = first_ty_ref { 331 | while let Type::Group(ty) = first_ty { 332 | first_ty = *ty.elem; 333 | } 334 | if let Type::Path(TypePath { qself: None, path }) = first_ty { 335 | trait_ = Some((None, path, for_token)); 336 | } else { 337 | unreachable!(); 338 | } 339 | } else { 340 | return Err(Error::new(for_token.span(), "for without target trait")); 341 | } 342 | self_ty = input.parse()?; 343 | } else { 344 | trait_ = None; 345 | self_ty = first_ty; 346 | } 347 | 348 | generics.where_clause = input.parse()?; 349 | 350 | if self_ty != parse_quote! { Self } { 351 | if let Some(ident) = in_ident { 352 | if !matches!(self_ty, Type::Path(TypePath { 353 | qself: None, 354 | path: syn::Path { 355 | leading_colon: None, 356 | ref segments, 357 | } 358 | }) if segments.len() == 1 && segments.first().unwrap().ident == *ident) 359 | { 360 | abort!( 361 | self_ty.span(), 362 | format!( 363 | "expected `Self` or `{0}` or `{0}<...>` or `Trait for Self`, etc", 364 | ident 365 | ) 366 | ); 367 | } 368 | } else { 369 | abort!(self_ty.span(), "expected `Self` or `Trait for Self`"); 370 | } 371 | } 372 | 373 | let content; 374 | let brace_token = braced!(content in input); 375 | attrs.extend(Attribute::parse_inner(&content)?); 376 | 377 | let mut items = Vec::new(); 378 | while !content.is_empty() { 379 | items.push(content.parse()?); 380 | } 381 | 382 | Ok(ItemImpl { 383 | attrs, 384 | defaultness, 385 | unsafety, 386 | impl_token, 387 | generics, 388 | trait_, 389 | self_ty: Box::new(self_ty), 390 | brace_token, 391 | items, 392 | }) 393 | } 394 | 395 | impl AnonField { 396 | fn check_is_fixed(ty: &Type, input_span: Span) -> Result<()> { 397 | let is_fixed = match ty { 398 | Type::ImplTrait(_) | Type::Infer(_) => false, 399 | ty => { 400 | struct IsFixed(bool); 401 | let mut checker = IsFixed(true); 402 | 403 | impl<'ast> syn::visit::Visit<'ast> for IsFixed { 404 | fn visit_type(&mut self, node: &'ast Type) { 405 | if matches!(node, Type::ImplTrait(_) | Type::Infer(_)) { 406 | self.0 = false; 407 | } 408 | } 409 | } 410 | syn::visit::visit_type(&mut checker, ty); 411 | 412 | checker.0 413 | } 414 | }; 415 | 416 | if is_fixed { 417 | Ok(()) 418 | } else { 419 | Err(Error::new( 420 | input_span, 421 | "require either a fixed type or a value assignment", 422 | )) 423 | } 424 | } 425 | 426 | fn parse_named(input: ParseStream) -> Result { 427 | let attrs = input.call(Attribute::parse_outer)?; 428 | let vis = input.parse()?; 429 | 430 | let ident = if input.peek(Token![_]) { 431 | let _: Token![_] = input.parse()?; 432 | None 433 | } else { 434 | Some(input.parse::()?) 435 | }; 436 | 437 | let mut colon_token = None; 438 | 439 | // Note: Colon matches `::` but that results in confusing error messages 440 | let ty = if input.peek(Colon) && !input.peek2(Colon) { 441 | colon_token = Some(input.parse()?); 442 | input.parse()? 443 | } else { 444 | parse_quote! { _ } 445 | }; 446 | 447 | let mut assignment = None; 448 | if let Ok(eq) = input.parse::() { 449 | assignment = Some((eq, input.parse()?)); 450 | } else { 451 | Self::check_is_fixed(&ty, input.span())?; 452 | } 453 | 454 | Ok(AnonField { 455 | attrs, 456 | vis, 457 | ident, 458 | colon_token, 459 | ty, 460 | assignment, 461 | }) 462 | } 463 | 464 | fn parse_unnamed(input: ParseStream) -> Result { 465 | let attrs = input.call(Attribute::parse_outer)?; 466 | let vis = input.parse()?; 467 | 468 | let ty = input.parse()?; 469 | 470 | let mut assignment = None; 471 | if let Ok(eq) = input.parse::() { 472 | assignment = Some((eq, input.parse()?)); 473 | } else { 474 | Self::check_is_fixed(&ty, input.span())?; 475 | } 476 | 477 | Ok(AnonField { 478 | attrs, 479 | vis, 480 | ident: None, 481 | colon_token: None, 482 | ty, 483 | assignment, 484 | }) 485 | } 486 | } 487 | 488 | impl Parse for Anon { 489 | fn parse(input: ParseStream) -> Result { 490 | let attrs = input.call(Attribute::parse_outer)?; 491 | let token = input.parse::()?; 492 | 493 | let mut generics = input.parse::()?; 494 | 495 | let mut lookahead = input.lookahead1(); 496 | if lookahead.peek(Token![where]) { 497 | generics.where_clause = Some(input.parse()?); 498 | lookahead = input.lookahead1(); 499 | } 500 | 501 | let style; 502 | let fields; 503 | if generics.where_clause.is_none() && lookahead.peek(Paren) { 504 | let content; 505 | let paren_token = parenthesized!(content in input); 506 | fields = content.parse_terminated(AnonField::parse_unnamed, Token![,])?; 507 | 508 | lookahead = input.lookahead1(); 509 | if lookahead.peek(Token![where]) { 510 | generics.where_clause = Some(input.parse()?); 511 | lookahead = input.lookahead1(); 512 | } 513 | 514 | if lookahead.peek(Semi) { 515 | style = StructStyle::Tuple(paren_token, input.parse()?); 516 | } else { 517 | return Err(lookahead.error()); 518 | } 519 | } else if lookahead.peek(Brace) { 520 | let content; 521 | let brace_token = braced!(content in input); 522 | style = StructStyle::Regular(brace_token); 523 | fields = content.parse_terminated(AnonField::parse_named, Token![,])?; 524 | } else if lookahead.peek(Semi) { 525 | style = StructStyle::Unit(input.parse()?); 526 | fields = Punctuated::new(); 527 | } else { 528 | return Err(lookahead.error()); 529 | } 530 | 531 | let mut impls = Vec::new(); 532 | while !input.is_empty() { 533 | impls.push(parse_impl(None, input)?); 534 | } 535 | 536 | Ok(Anon { 537 | attrs, 538 | token, 539 | generics, 540 | style, 541 | fields, 542 | impls, 543 | }) 544 | } 545 | } 546 | } 547 | -------------------------------------------------------------------------------- /lib/src/autoimpl.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! Implementation of the `#[autoimpl]` attribute 7 | 8 | use crate::generics::{clause_to_toks, WhereClause}; 9 | use crate::SimplePath; 10 | use proc_macro2::{Span, TokenStream as Toks}; 11 | use proc_macro_error2::emit_error; 12 | use quote::{quote, TokenStreamExt}; 13 | use syn::spanned::Spanned; 14 | use syn::token::Comma; 15 | use syn::{ 16 | parse2, Field, Fields, Ident, Index, Item, ItemEnum, ItemStruct, Member, Path, PathArguments, 17 | Token, 18 | }; 19 | 20 | mod for_deref; 21 | mod impl_misc; 22 | mod impl_using; 23 | 24 | pub use for_deref::ForDeref; 25 | pub use impl_misc::*; 26 | pub use impl_using::*; 27 | 28 | /// List of all builtin trait implementations 29 | pub const STD_IMPLS: &[&dyn ImplTrait] = &[ 30 | &ImplClone, 31 | &ImplCopy, 32 | &ImplDebug, 33 | &ImplDefault, 34 | &ImplPartialEq, 35 | &ImplEq, 36 | &ImplPartialOrd, 37 | &ImplOrd, 38 | &ImplHash, 39 | &ImplBorrow, 40 | &ImplBorrowMut, 41 | &ImplAsRef, 42 | &ImplAsMut, 43 | &ImplDeref, 44 | &ImplDerefMut, 45 | ]; 46 | 47 | /// Trait required by extensions 48 | pub trait ImplTrait { 49 | /// Trait path 50 | /// 51 | /// This path is matched against trait names in `#[autoimpl]` parameters. 52 | fn path(&self) -> SimplePath; 53 | 54 | /// True if this target supports path arguments 55 | fn support_path_arguments(&self) -> bool { 56 | false 57 | } 58 | 59 | /// True if this target supports ignoring fields 60 | /// 61 | /// Default implementation: `false` 62 | fn support_ignore(&self) -> bool { 63 | false 64 | } 65 | 66 | /// If the target does not support `ignore` but does tolerate `ignore` in 67 | /// the presence of another target (e.g. `autoimpl(Eq, PartialEq ignore self.foo)`), 68 | /// return the path of that other target here. 69 | fn allow_ignore_with(&self) -> Option { 70 | None 71 | } 72 | 73 | /// True if this target supports using a field 74 | /// 75 | /// Default implementation: `false` 76 | fn support_using(&self) -> bool { 77 | false 78 | } 79 | 80 | /// Generate an impl for an enum item 81 | /// 82 | /// The default implementation is a wrapper around [`Self::enum_items`] 83 | /// and suffices for most cases. It may be overridden, e.g. to generate 84 | /// multiple implementation items. It is not recommended to modify the 85 | /// generics. 86 | fn enum_impl(&self, item: &ItemEnum, args: &ImplArgs) -> Result { 87 | let type_ident = &item.ident; 88 | let (impl_generics, ty_generics, item_wc) = item.generics.split_for_impl(); 89 | 90 | let (path, items) = self.enum_items(item, args)?; 91 | 92 | let wc = clause_to_toks(&args.clause, item_wc, &path); 93 | 94 | Ok(quote! { 95 | #[automatically_derived] 96 | impl #impl_generics #path for #type_ident #ty_generics #wc { 97 | #items 98 | } 99 | }) 100 | } 101 | 102 | /// Generate an impl for a struct item 103 | /// 104 | /// The default implementation is a wrapper around [`Self::struct_items`] 105 | /// and suffices for most cases. It may be overridden, e.g. to generate 106 | /// multiple implementation items. It is not recommended to modify the 107 | /// generics. 108 | fn struct_impl(&self, item: &ItemStruct, args: &ImplArgs) -> Result { 109 | let type_ident = &item.ident; 110 | let (impl_generics, ty_generics, item_wc) = item.generics.split_for_impl(); 111 | 112 | let (path, items) = self.struct_items(item, args)?; 113 | 114 | let wc = clause_to_toks(&args.clause, item_wc, &path); 115 | 116 | Ok(quote! { 117 | #[automatically_derived] 118 | impl #impl_generics #path for #type_ident #ty_generics #wc { 119 | #items 120 | } 121 | }) 122 | } 123 | 124 | /// Generate enum items 125 | /// 126 | /// On success, this method returns the tuple `(trait_path, items)`. These 127 | /// are used to generate the following implementation: 128 | /// ```ignore 129 | /// impl #impl_generics #trait_path for #type_ident #ty_generics #where_clause { 130 | /// #items 131 | /// } 132 | /// ``` 133 | /// 134 | /// Note: this method is *only* called by the default implementation of [`Self::enum_impl`]. 135 | /// 136 | /// Default implementation: returns an error indicating that enum expansion is not supported. 137 | fn enum_items(&self, item: &ItemEnum, args: &ImplArgs) -> Result<(Toks, Toks)> { 138 | let _ = (item, args); 139 | Err(Error::CallSite("enum expansion not supported")) 140 | } 141 | 142 | /// Generate struct items 143 | /// 144 | /// On success, this method returns the tuple `(trait_path, items)`. These 145 | /// are used to generate the following implementation: 146 | /// ```ignore 147 | /// impl #impl_generics #trait_path for #type_ident #ty_generics #where_clause { 148 | /// #items 149 | /// } 150 | /// ``` 151 | /// 152 | /// Note: this method is *only* called by the default implementation of [`Self::struct_impl`]. 153 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)>; 154 | } 155 | 156 | #[allow(non_camel_case_types)] 157 | mod kw { 158 | use syn::custom_keyword; 159 | 160 | custom_keyword!(ignore); 161 | custom_keyword!(using); 162 | } 163 | 164 | /// The `#[autoimpl]` attribute 165 | pub enum Attr { 166 | /// Autoimpl for types supporting `Deref` 167 | ForDeref(ForDeref), 168 | /// Autoimpl for trait targets 169 | ImplTraits(ImplTraits), 170 | } 171 | 172 | /// Autoimpl for trait targets 173 | pub struct ImplTraits { 174 | targets: Vec, 175 | args: ImplArgs, 176 | } 177 | 178 | /// Error type 179 | pub enum Error { 180 | /// Emit an error clarifying that `using self.FIELD` is required 181 | RequireUsing, 182 | /// Emit an error over the target trait name using `message` 183 | CallSite(&'static str), 184 | /// Emit an error with the given `span` and `message` 185 | WithSpan(Span, &'static str), 186 | /// Emit an error regarding path arguments 187 | PathArguments(&'static str), 188 | } 189 | 190 | impl Error { 191 | /// Report via [`proc_macro_error2::emit_error`]. 192 | pub fn emit(self, target: Span, path_args: Span) { 193 | match self { 194 | Error::RequireUsing => { 195 | emit_error!(target, "target requires argument `using self.FIELD`") 196 | } 197 | Error::CallSite(msg) => emit_error!(target, msg), 198 | Error::WithSpan(span, msg) => emit_error!(span, msg), 199 | Error::PathArguments(msg) => emit_error!(path_args, msg), 200 | } 201 | } 202 | } 203 | 204 | /// Result type 205 | pub type Result = std::result::Result; 206 | 207 | mod parsing { 208 | use super::*; 209 | use syn::parse::{Parse, ParseStream, Result}; 210 | 211 | impl Parse for Attr { 212 | fn parse(input: ParseStream) -> Result { 213 | let mut empty_or_trailing = true; 214 | let mut lookahead = input.lookahead1(); 215 | 216 | if lookahead.peek(Token![for]) { 217 | return input.call(ForDeref::parse).map(Attr::ForDeref); 218 | } 219 | 220 | let mut targets = Vec::new(); 221 | let mut using = None; 222 | let mut ignores = Vec::new(); 223 | let mut clause = None; 224 | 225 | while !input.is_empty() { 226 | if lookahead.peek(Token![where]) 227 | || lookahead.peek(kw::using) 228 | || lookahead.peek(kw::ignore) 229 | { 230 | break; 231 | } 232 | 233 | if empty_or_trailing { 234 | if lookahead.peek(Ident) { 235 | targets.push(input.parse()?); 236 | empty_or_trailing = false; 237 | lookahead = input.lookahead1(); 238 | continue; 239 | } 240 | } else if input.peek(Comma) { 241 | let _ = input.parse::()?; 242 | empty_or_trailing = true; 243 | lookahead = input.lookahead1(); 244 | continue; 245 | } 246 | return Err(lookahead.error()); 247 | } 248 | 249 | while !input.is_empty() { 250 | lookahead = input.lookahead1(); 251 | if clause.is_none() && using.is_none() && lookahead.peek(kw::using) { 252 | let _: kw::using = input.parse()?; 253 | let _ = input.parse::()?; 254 | let _ = input.parse::()?; 255 | using = Some(input.parse()?); 256 | } else if clause.is_none() && ignores.is_empty() && lookahead.peek(kw::ignore) { 257 | let _: kw::ignore = input.parse()?; 258 | let _ = input.parse::()?; 259 | let _ = input.parse::()?; 260 | ignores.push(input.parse()?); 261 | while input.peek(Comma) { 262 | let _ = input.parse::()?; 263 | if input.peek(Token![self]) { 264 | let _ = input.parse::()?; 265 | let _ = input.parse::()?; 266 | ignores.push(input.parse()?); 267 | continue; 268 | } 269 | break; 270 | } 271 | } else if lookahead.peek(Token![where]) { 272 | // Note: assigning to clause disables other match branches since clause must come last! 273 | clause = Some(input.parse()?); 274 | } else { 275 | return Err(lookahead.error()); 276 | } 277 | } 278 | 279 | let args = ImplArgs { 280 | path_arguments: PathArguments::None, 281 | ignores, 282 | using, 283 | clause, 284 | }; 285 | Ok(Attr::ImplTraits(ImplTraits { targets, args })) 286 | } 287 | } 288 | } 289 | 290 | impl ImplTraits { 291 | /// Expand over the given `item` 292 | /// 293 | /// This attribute does not modify the item. 294 | /// The caller should append the result to `item` tokens. 295 | /// 296 | /// Errors are reported via [`proc_macro_error2::emit_error`]. 297 | pub fn expand( 298 | self, 299 | item: Toks, 300 | find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>, 301 | ) -> Toks { 302 | match parse2::(item) { 303 | Ok(Item::Enum(item)) => self.expand_enum(item, find_impl), 304 | Ok(Item::Struct(item)) => self.expand_struct(item, find_impl), 305 | Ok(item) => { 306 | emit_error!(item, "expected struct"); 307 | Toks::new() 308 | } 309 | Err(err) => err.into_compile_error(), 310 | } 311 | } 312 | 313 | fn expand_enum( 314 | self, 315 | item: ItemEnum, 316 | find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>, 317 | ) -> Toks { 318 | let ImplTraits { 319 | mut targets, 320 | mut args, 321 | } = self; 322 | 323 | if !args.ignores.is_empty() { 324 | let ignores = args.ignores.iter(); 325 | let list = quote! { #(#ignores)* }; 326 | emit_error!(list, "enum expansion does not currently support `ignore`",); 327 | return Toks::new(); 328 | } 329 | if let Some(mem) = args.using { 330 | emit_error!(mem, "enum expansion does not currently support `using`",); 331 | return Toks::new(); 332 | } 333 | 334 | let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len()); 335 | for mut target in targets.drain(..) { 336 | let target_span = target.span(); 337 | let path_args = target 338 | .segments 339 | .last_mut() 340 | .map(|seg| std::mem::take(&mut seg.arguments)) 341 | .unwrap_or(PathArguments::None); 342 | let target_impl = match find_impl(&target) { 343 | Some(impl_) => impl_, 344 | None => { 345 | emit_error!(target, "unsupported trait"); 346 | return Toks::new(); 347 | } 348 | }; 349 | 350 | if !(path_args.is_empty() || target_impl.support_path_arguments()) { 351 | emit_error!( 352 | target_span, 353 | "target {} does not support path arguments", 354 | target_impl.path() 355 | ); 356 | } 357 | 358 | impl_targets.push((target.span(), target_impl, path_args)); 359 | } 360 | 361 | let mut toks = Toks::new(); 362 | 363 | for (span, target, path_args) in impl_targets.drain(..) { 364 | let path_args_span = path_args.span(); 365 | args.path_arguments = path_args; 366 | match target.enum_impl(&item, &args) { 367 | Ok(items) => toks.append_all(items), 368 | Err(error) => error.emit(span, path_args_span), 369 | } 370 | } 371 | toks 372 | } 373 | 374 | fn expand_struct( 375 | self, 376 | item: ItemStruct, 377 | find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>, 378 | ) -> Toks { 379 | let ImplTraits { 380 | mut targets, 381 | mut args, 382 | } = self; 383 | 384 | let mut not_supporting_ignore = vec![]; 385 | let mut not_supporting_using = vec![]; 386 | 387 | let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len()); 388 | for mut target in targets.drain(..) { 389 | let target_span = target.span(); 390 | let path_args = target 391 | .segments 392 | .last_mut() 393 | .map(|seg| std::mem::take(&mut seg.arguments)) 394 | .unwrap_or(PathArguments::None); 395 | let target_impl = match find_impl(&target) { 396 | Some(impl_) => impl_, 397 | None => { 398 | emit_error!(target, "unsupported trait"); 399 | return Toks::new(); 400 | } 401 | }; 402 | 403 | if !target_impl.support_ignore() { 404 | let except_with = target_impl.allow_ignore_with(); 405 | not_supporting_ignore.push((target.clone(), except_with)); 406 | } 407 | if !target_impl.support_using() { 408 | not_supporting_using.push(target.clone()); 409 | } 410 | if !(path_args.is_empty() || target_impl.support_path_arguments()) { 411 | emit_error!( 412 | target_span, 413 | "target {} does not support path arguments", 414 | target_impl.path() 415 | ); 416 | } 417 | 418 | impl_targets.push((target.span(), target_impl, path_args)); 419 | } 420 | 421 | if !args.ignores.is_empty() { 422 | for (target, except_with) in not_supporting_ignore.into_iter() { 423 | if let Some(path) = except_with { 424 | if impl_targets 425 | .iter() 426 | .any(|(_, target_impl, _)| path == target_impl.path()) 427 | { 428 | continue; 429 | } 430 | } 431 | emit_error!(target, "target does not support `ignore`",); 432 | } 433 | } 434 | if args.using.is_some() { 435 | for target in not_supporting_using.into_iter() { 436 | emit_error!(target, "target does not support `using`",); 437 | } 438 | } 439 | 440 | fn check_is_field(mem: &Member, fields: &Fields) { 441 | match (fields, mem) { 442 | (Fields::Named(fields), Member::Named(ref ident)) => { 443 | if fields 444 | .named 445 | .iter() 446 | .any(|field| field.ident.as_ref() == Some(ident)) 447 | { 448 | return; 449 | } 450 | } 451 | (Fields::Unnamed(fields), Member::Unnamed(index)) => { 452 | if (index.index as usize) < fields.unnamed.len() { 453 | return; 454 | } 455 | } 456 | _ => (), 457 | } 458 | emit_error!(mem, "not a struct field"); 459 | } 460 | 461 | let mut toks = Toks::new(); 462 | for mem in &args.ignores { 463 | check_is_field(mem, &item.fields); 464 | } 465 | if let Some(mem) = args.using_member() { 466 | check_is_field(mem, &item.fields); 467 | } 468 | 469 | for (span, target, path_args) in impl_targets.drain(..) { 470 | let path_args_span = path_args.span(); 471 | args.path_arguments = path_args; 472 | match target.struct_impl(&item, &args) { 473 | Ok(items) => toks.append_all(items), 474 | Err(error) => error.emit(span, path_args_span), 475 | } 476 | } 477 | toks 478 | } 479 | } 480 | 481 | /// Arguments passed to [`ImplTrait`] implementation methods 482 | pub struct ImplArgs { 483 | /// Path arguments to trait 484 | /// 485 | /// Example: if the target is `Deref`, this is ``. 486 | /// This is always empty unless [`ImplTrait::support_path_arguments`] returns true. 487 | pub path_arguments: PathArguments, 488 | /// Fields ignored in attribute 489 | pub ignores: Vec, 490 | /// Field specified to 'use' in attribute 491 | pub using: Option, 492 | /// Where clause added to attribute 493 | pub clause: Option, 494 | } 495 | 496 | impl ImplArgs { 497 | /// If true, this field is ignored 498 | pub fn ignore(&self, member: &Member) -> bool { 499 | self.ignores.iter().any(|ig| *ig == *member) 500 | } 501 | 502 | /// If true, this named field is ignored 503 | pub fn ignore_named(&self, ident: &Ident) -> bool { 504 | self.ignores.iter().any(|ig| match ig { 505 | Member::Named(m) => m == ident, 506 | _ => false, 507 | }) 508 | } 509 | 510 | /// If true, this unnamed field is ignored 511 | pub fn ignore_unnamed(&self, index: &Index) -> bool { 512 | self.ignores.iter().any(|ig| match ig { 513 | Member::Unnamed(m) => m == index, 514 | _ => false, 515 | }) 516 | } 517 | 518 | /// Field to "use", if any 519 | pub fn using_member(&self) -> Option<&Member> { 520 | self.using.as_ref() 521 | } 522 | 523 | /// Find field to "use", if any 524 | pub fn using_field<'b>(&self, fields: &'b Fields) -> Option<&'b Field> { 525 | match fields { 526 | Fields::Named(fields) => fields.named.iter().find(|field| match self.using { 527 | Some(Member::Named(ref ident)) => ident == field.ident.as_ref().unwrap(), 528 | _ => false, 529 | }), 530 | Fields::Unnamed(fields) => { 531 | fields 532 | .unnamed 533 | .iter() 534 | .enumerate() 535 | .find_map(|(i, field)| match self.using { 536 | Some(Member::Unnamed(ref index)) => { 537 | (*index == Index::from(i)).then(|| field) 538 | } 539 | _ => None, 540 | }) 541 | } 542 | Fields::Unit => None, 543 | } 544 | } 545 | 546 | /// Call the given closure over all non-ignored fields 547 | pub fn for_fields<'f>(&self, fields: &'f Fields, f: impl FnMut(Member, &'f Field)) { 548 | self.for_fields_iter(fields.iter().enumerate(), f); 549 | } 550 | 551 | /// Call the given closure over all non-ignored fields 552 | pub fn for_fields_iter<'f>( 553 | &self, 554 | fields: impl Iterator, 555 | mut f: impl FnMut(Member, &'f Field), 556 | ) { 557 | for (i, field) in fields { 558 | let member = match field.ident.clone() { 559 | Some(ident) => Member::Named(ident), 560 | None => Member::Unnamed(Index::from(i)), 561 | }; 562 | if !self.ignore(&member) { 563 | f(member, field); 564 | } 565 | } 566 | } 567 | } 568 | -------------------------------------------------------------------------------- /lib/src/autoimpl/for_deref.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! Implementation of the `#[autoimpl]` attribute 7 | 8 | use crate::generics::{GenericParam, Generics, TypeParamBound, WherePredicate}; 9 | use proc_macro2::{Span, TokenStream}; 10 | use proc_macro_error2::{emit_call_site_error, emit_error}; 11 | use quote::{quote, ToTokens, TokenStreamExt}; 12 | use syn::punctuated::Punctuated; 13 | use syn::spanned::Spanned; 14 | use syn::token::{Comma, Eq, PathSep}; 15 | use syn::{parse_quote, FnArg, Ident, Item, Pat, Token, TraitItem, Type, TypePath}; 16 | 17 | /// Autoimpl for types supporting `Deref` 18 | pub struct ForDeref { 19 | generics: Generics, 20 | definitive: Ident, 21 | targets: Punctuated, 22 | } 23 | 24 | mod parsing { 25 | use super::*; 26 | use syn::parse::{Error, Parse, ParseStream, Result}; 27 | 28 | impl Parse for ForDeref { 29 | fn parse(input: ParseStream) -> Result { 30 | let _ = input.parse::()?; 31 | let mut generics: Generics = input.parse()?; 32 | 33 | let targets = Punctuated::parse_separated_nonempty(input)?; 34 | 35 | let mut lookahead = input.lookahead1(); 36 | if lookahead.peek(Token![where]) { 37 | generics.where_clause = Some(input.parse()?); 38 | lookahead = input.lookahead1(); 39 | } 40 | 41 | if !input.is_empty() { 42 | return Err(lookahead.error()); 43 | } 44 | 45 | let mut definitive: Option = None; 46 | for param in &generics.params { 47 | if let GenericParam::Type(param) = param { 48 | for bound in ¶m.bounds { 49 | if matches!(bound, TypeParamBound::TraitSubst(_)) { 50 | definitive = Some(param.ident.clone()); 51 | break; 52 | } 53 | } 54 | } 55 | } 56 | if definitive.is_none() { 57 | if let Some(clause) = generics.where_clause.as_ref() { 58 | for pred in &clause.predicates { 59 | if let WherePredicate::Type(pred) = pred { 60 | for bound in &pred.bounds { 61 | if matches!(bound, TypeParamBound::TraitSubst(_)) { 62 | if let Type::Path(TypePath { qself: None, path }) = 63 | &pred.bounded_ty 64 | { 65 | if let Some(ident) = path.get_ident() { 66 | definitive = Some(ident.clone()); 67 | break; 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | let definitive = match definitive { 77 | Some(def) => def, 78 | None => { 79 | return Err(Error::new( 80 | Span::call_site(), 81 | "no definitive type parameter: require a parameter bound like `T: trait``", 82 | )); 83 | } 84 | }; 85 | 86 | Ok(ForDeref { 87 | generics, 88 | definitive, 89 | targets, 90 | }) 91 | } 92 | } 93 | } 94 | 95 | // HACK: there is no definitive determination of which attributes should be 96 | // emitted on the generated impl fn items. We use a whitelist. 97 | fn propegate_attr_to_impl(attr: &syn::Attribute) -> bool { 98 | let path = attr.path().to_token_stream().to_string(); 99 | matches!(path.as_str(), "cfg" | "allow" | "warn" | "deny" | "forbid") 100 | } 101 | 102 | fn has_bound_on_self(gen: &syn::Generics) -> bool { 103 | if let Some(ref clause) = gen.where_clause { 104 | for pred in clause.predicates.iter() { 105 | if let syn::WherePredicate::Type(ref ty) = pred { 106 | if let Type::Path(ref bounded) = ty.bounded_ty { 107 | if bounded.qself.is_none() && bounded.path.is_ident("Self") { 108 | if ty 109 | .bounds 110 | .iter() 111 | .any(|bound| matches!(bound, syn::TypeParamBound::Trait(_))) 112 | { 113 | return true; 114 | } 115 | } 116 | } 117 | } 118 | // Note: we ignore lifetime bounds, since Self: 'a implies that 'a 119 | // is a parameter with lifetime shorter than Self (thus is more a 120 | // bound on 'a than it is on Self), while 'a: Self is not supported. 121 | } 122 | } 123 | 124 | false 125 | } 126 | 127 | impl ForDeref { 128 | /// Expand over the given `item` 129 | /// 130 | /// This attribute does not modify the item. 131 | /// The caller should append the result to `item` tokens. 132 | pub fn expand(self, item: TokenStream) -> TokenStream { 133 | let trait_def = match syn::parse2::(item) { 134 | Ok(Item::Trait(item)) => item, 135 | Ok(item) => { 136 | emit_error!(item, "expected trait"); 137 | return TokenStream::new(); 138 | } 139 | Err(err) => return err.into_compile_error(), 140 | }; 141 | 142 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 143 | enum Bound { 144 | None, 145 | Deref(bool), // true if DerefMut 146 | ErrorEmitted, 147 | } 148 | let mut bound = Bound::None; 149 | 150 | let trait_ident = &trait_def.ident; 151 | let (_, trait_generics, _) = trait_def.generics.split_for_impl(); 152 | let trait_ty = quote! { #trait_ident #trait_generics }; 153 | let ty_generics = self.generics.ty_generics(&trait_def.generics); 154 | let (impl_generics, where_clause) = 155 | self.generics.impl_generics(&trait_def.generics, &trait_ty); 156 | 157 | let definitive_ty = self.definitive; 158 | let definitive = quote! { < #definitive_ty as #trait_ty > }; 159 | 160 | // Tokenize, like ToTokens impls for syn::TraitItem*, but for definition 161 | let mut impl_items = TokenStream::new(); 162 | let tokens = &mut impl_items; 163 | for item in trait_def.items.into_iter() { 164 | match item { 165 | TraitItem::Const(item) => { 166 | for attr in item.attrs.iter() { 167 | if *attr.path() == parse_quote! { cfg } { 168 | attr.to_tokens(tokens); 169 | } 170 | } 171 | 172 | item.const_token.to_tokens(tokens); 173 | item.ident.to_tokens(tokens); 174 | item.colon_token.to_tokens(tokens); 175 | item.ty.to_tokens(tokens); 176 | 177 | Eq::default().to_tokens(tokens); 178 | definitive.to_tokens(tokens); 179 | PathSep::default().to_tokens(tokens); 180 | item.ident.to_tokens(tokens); 181 | 182 | item.semi_token.to_tokens(tokens); 183 | } 184 | TraitItem::Fn(mut item) => { 185 | for attr in item.attrs.iter() { 186 | if propegate_attr_to_impl(attr) { 187 | attr.to_tokens(tokens); 188 | } 189 | } 190 | 191 | if has_bound_on_self(&item.sig.generics) { 192 | // If the method has a bound on Self, we cannot use a dereferencing 193 | // implementation since the definitive type is not guaranteed to match 194 | // the bound (we also cannot add a bound). 195 | 196 | if item.default.is_none() { 197 | emit_call_site_error!( 198 | "cannot autoimpl trait with Deref"; 199 | note = item.span() => "method has a bound on Self and no default implementation"; 200 | ); 201 | } 202 | 203 | continue; 204 | } 205 | 206 | for (i, arg) in item.sig.inputs.iter_mut().enumerate() { 207 | if let FnArg::Typed(ref mut ty) = arg { 208 | if let Pat::Ident(pat) = &mut *ty.pat { 209 | // We can keep the ident but must not use `ref` / `mut` modifiers 210 | pat.by_ref = None; 211 | pat.mutability = None; 212 | assert_eq!(pat.subpat, None); 213 | } else { 214 | // Substitute a fresh ident 215 | let name = format!("arg{i}"); 216 | let ident = Ident::new(&name, Span::call_site()); 217 | ty.pat = Box::new(Pat::Ident(syn::PatIdent { 218 | attrs: vec![], 219 | by_ref: None, 220 | mutability: None, 221 | ident, 222 | subpat: None, 223 | })); 224 | } 225 | } 226 | } 227 | item.sig.to_tokens(tokens); 228 | 229 | bound = bound.max(match item.sig.inputs.first() { 230 | Some(FnArg::Receiver(rec)) => { 231 | if rec.reference.is_some() { 232 | Bound::Deref(rec.mutability.is_some()) 233 | } else { 234 | emit_call_site_error!( 235 | "cannot autoimpl trait with Deref"; 236 | note = rec.span() => "deref cannot yield `self` by value"; 237 | ); 238 | Bound::ErrorEmitted 239 | } 240 | } 241 | Some(FnArg::Typed(ref pat)) => match &*pat.ty { 242 | Type::Reference(rf) if rf.elem == parse_quote! { Self } => { 243 | Bound::Deref(rf.mutability.is_some()) 244 | } 245 | _ => Bound::None, 246 | }, 247 | _ => Bound::None, 248 | }); 249 | 250 | let ident = &item.sig.ident; 251 | let params = item.sig.inputs.iter().map(|arg| { 252 | let mut toks = TokenStream::new(); 253 | match arg { 254 | FnArg::Receiver(arg) => { 255 | for attr in &arg.attrs { 256 | if propegate_attr_to_impl(&attr) { 257 | attr.to_tokens(&mut toks); 258 | } 259 | } 260 | arg.self_token.to_tokens(&mut toks); 261 | } 262 | FnArg::Typed(arg) => { 263 | for attr in &arg.attrs { 264 | if propegate_attr_to_impl(&attr) { 265 | attr.to_tokens(&mut toks); 266 | }; 267 | } 268 | 269 | arg.pat.to_tokens(&mut toks); 270 | } 271 | }; 272 | toks 273 | }); 274 | tokens.append_all(quote! { { 275 | #definitive :: #ident ( #(#params),* ) 276 | } }); 277 | } 278 | TraitItem::Type(item) => { 279 | for attr in item.attrs.iter() { 280 | if *attr.path() == parse_quote! { cfg } { 281 | attr.to_tokens(tokens); 282 | } 283 | } 284 | 285 | if has_bound_on_self(&item.generics) { 286 | emit_call_site_error!( 287 | "cannot autoimpl trait with Deref"; 288 | note = item.span() => "type has a bound on Self"; 289 | ); 290 | } 291 | 292 | item.type_token.to_tokens(tokens); 293 | item.ident.to_tokens(tokens); 294 | 295 | let (_, ty_generics, where_clause) = item.generics.split_for_impl(); 296 | ty_generics.to_tokens(tokens); 297 | 298 | Eq::default().to_tokens(tokens); 299 | definitive.to_tokens(tokens); 300 | PathSep::default().to_tokens(tokens); 301 | item.ident.to_tokens(tokens); 302 | ty_generics.to_tokens(tokens); 303 | 304 | where_clause.to_tokens(tokens); 305 | item.semi_token.to_tokens(tokens); 306 | } 307 | TraitItem::Macro(item) => { 308 | emit_error!(item, "unsupported: macro item in trait"); 309 | } 310 | TraitItem::Verbatim(item) => { 311 | emit_error!(item, "unsupported: verbatim item in trait"); 312 | } 313 | 314 | /* Testing of exhaustive matching is disabled: syn 1.0.90 breaks it. 315 | #[cfg(test)] 316 | TraitItem::__TestExhaustive(_) => unimplemented!(), 317 | #[cfg(not(test))] 318 | */ 319 | _ => (), 320 | } 321 | } 322 | 323 | let mut toks = TokenStream::new(); 324 | match bound { 325 | Bound::None => (), 326 | Bound::Deref(is_mut) => { 327 | // Emit a bound to improve error messages (see issue 27) 328 | let bound = match is_mut { 329 | false => quote! { ::core::ops::Deref }, 330 | true => quote! { ::core::ops::DerefMut }, 331 | }; 332 | 333 | let target_impls = self.targets.iter().map(|target| { 334 | quote! { 335 | impl #impl_generics TargetMustImplDeref #ty_generics for #target 336 | #where_clause {} 337 | } 338 | }); 339 | 340 | toks.append_all(quote! { 341 | #[automatically_derived] 342 | const _: () = { 343 | trait TargetMustImplDeref #impl_generics: #bound 344 | #where_clause {} 345 | 346 | #(#target_impls)* 347 | }; 348 | }); 349 | } 350 | Bound::ErrorEmitted => return toks, 351 | } 352 | 353 | for target in self.targets { 354 | toks.append_all(quote! { 355 | #[automatically_derived] 356 | impl #impl_generics #trait_ty for #target #where_clause { 357 | #impl_items 358 | } 359 | }); 360 | } 361 | toks 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /lib/src/autoimpl/impl_misc.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! Miscellaneous impls 7 | 8 | use super::{ImplArgs, ImplTrait, Result}; 9 | use crate::{IdentFormatter, SimplePath}; 10 | use proc_macro2::TokenStream as Toks; 11 | use quote::{quote, ToTokens, TokenStreamExt}; 12 | use syn::{Fields, Index, ItemEnum, ItemStruct, Member, Token}; 13 | 14 | /// Implement [`core::clone::Clone`] 15 | pub struct ImplClone; 16 | impl ImplTrait for ImplClone { 17 | fn path(&self) -> SimplePath { 18 | SimplePath::new(&["", "core", "clone", "Clone"]) 19 | } 20 | 21 | fn support_ignore(&self) -> bool { 22 | true 23 | } 24 | 25 | fn enum_items(&self, item: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> { 26 | let mut idfmt = IdentFormatter::new(); 27 | let name = &item.ident; 28 | let mut variants = Toks::new(); 29 | for v in item.variants.iter() { 30 | let ident = &v.ident; 31 | let tag = quote! { #name :: #ident }; 32 | variants.append_all(match v.fields { 33 | Fields::Named(ref fields) => { 34 | let idents = fields.named.iter().map(|f| f.ident.as_ref().unwrap()); 35 | let clones = fields.named.iter().map(|f| { 36 | let ident = f.ident.as_ref().unwrap(); 37 | quote! { #ident: ::core::clone::Clone::clone(&#ident) } 38 | }); 39 | quote! { #tag { #(#idents),* } => #tag { #(#clones),* }, } 40 | } 41 | Fields::Unnamed(ref fields) => { 42 | let len = fields.unnamed.len(); 43 | let mut bindings = Vec::with_capacity(len); 44 | let mut items = Vec::with_capacity(len); 45 | for i in 0..len { 46 | let ident = idfmt.make_call_site(format_args!("_{i}")); 47 | bindings.push(quote! { ref #ident }); 48 | items.push(quote! { ::core::clone::Clone::clone(&#ident) }); 49 | } 50 | quote! { #tag ( #(#bindings),* ) => #tag ( #(#items),* ), } 51 | } 52 | Fields::Unit => quote! { #tag => #tag, }, 53 | }); 54 | } 55 | let method = quote! { 56 | fn clone(&self) -> Self { 57 | match *self { 58 | #variants 59 | } 60 | } 61 | }; 62 | Ok((quote! { ::core::clone::Clone }, method)) 63 | } 64 | 65 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 66 | let type_ident = &item.ident; 67 | let inner = match &item.fields { 68 | Fields::Named(fields) => { 69 | let mut toks = Toks::new(); 70 | for field in fields.named.iter() { 71 | let ident = field.ident.as_ref().unwrap(); 72 | if args.ignore_named(ident) { 73 | toks.append_all(quote! { #ident: Default::default(), }); 74 | } else { 75 | toks.append_all( 76 | quote! { #ident: ::core::clone::Clone::clone(&self.#ident), }, 77 | ); 78 | } 79 | } 80 | quote! { #type_ident { #toks } } 81 | } 82 | Fields::Unnamed(fields) => { 83 | let mut toks = Toks::new(); 84 | for i in 0..fields.unnamed.len() { 85 | let index = Index::from(i); 86 | if args.ignore_unnamed(&index) { 87 | toks.append_all(quote! { Default::default(), }); 88 | } else { 89 | toks.append_all(quote! { ::core::clone::Clone::clone(&self.#index), }); 90 | } 91 | } 92 | quote! { #type_ident ( #toks ) } 93 | } 94 | Fields::Unit => quote! { #type_ident }, 95 | }; 96 | let method = quote! { 97 | fn clone(&self) -> Self { 98 | #inner 99 | } 100 | }; 101 | Ok((quote! { ::core::clone::Clone }, method)) 102 | } 103 | } 104 | 105 | /// Implement [`core::marker::Copy`] 106 | pub struct ImplCopy; 107 | impl ImplTrait for ImplCopy { 108 | fn path(&self) -> SimplePath { 109 | SimplePath::new(&["", "core", "marker", "Copy"]) 110 | } 111 | 112 | fn allow_ignore_with(&self) -> Option { 113 | Some(SimplePath::new(&["", "core", "clone", "Clone"])) 114 | } 115 | 116 | fn enum_items(&self, _: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> { 117 | Ok((quote! { ::core::marker::Copy }, quote! {})) 118 | } 119 | 120 | fn struct_items(&self, _: &ItemStruct, _: &ImplArgs) -> Result<(Toks, Toks)> { 121 | Ok((quote! { ::core::marker::Copy }, quote! {})) 122 | } 123 | } 124 | 125 | /// Implement [`core::fmt::Debug`] 126 | pub struct ImplDebug; 127 | impl ImplTrait for ImplDebug { 128 | fn path(&self) -> SimplePath { 129 | SimplePath::new(&["", "core", "fmt", "Debug"]) 130 | } 131 | 132 | fn support_ignore(&self) -> bool { 133 | true 134 | } 135 | 136 | fn enum_items(&self, item: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> { 137 | let mut idfmt = IdentFormatter::new(); 138 | let name = &item.ident; 139 | let type_name = item.ident.to_string(); 140 | let mut variants = Toks::new(); 141 | for v in item.variants.iter() { 142 | let ident = &v.ident; 143 | let var_name = ident.to_string(); 144 | let tag = quote! { #name :: #ident }; 145 | variants.append_all(match v.fields { 146 | Fields::Named(ref fields) => { 147 | let idents = fields.named.iter().map(|f| f.ident.as_ref().unwrap()); 148 | let mut items = Toks::new(); 149 | for field in fields.named.iter() { 150 | let ident = field.ident.as_ref().unwrap(); 151 | let name = ident.to_string(); 152 | items.append_all(quote! { .field(#name, #ident) }); 153 | } 154 | quote! { 155 | #tag { #(ref #idents),* } => f.debug_struct(#var_name) #items .finish(), 156 | } 157 | } 158 | Fields::Unnamed(ref fields) => { 159 | let len = fields.unnamed.len(); 160 | let mut bindings = Vec::with_capacity(len); 161 | let mut items = Toks::new(); 162 | for i in 0..len { 163 | let ident = idfmt.make_call_site(format_args!("_{i}")); 164 | bindings.push(quote! { ref #ident }); 165 | items.append_all(quote! { .field(#ident) }); 166 | } 167 | quote! { 168 | #tag ( #(#bindings),* ) => f.debug_tuple(#var_name) #items .finish(), 169 | } 170 | } 171 | Fields::Unit => quote! { #tag => f.write_str(#var_name), }, 172 | }) 173 | } 174 | 175 | // Note: unlike #[derive(Debug)], we include the name of the enum! 176 | let method = quote! { 177 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 178 | write!(f, "{}::", #type_name)?; 179 | match *self { 180 | #variants 181 | } 182 | } 183 | }; 184 | Ok((quote! { ::core::fmt::Debug }, method)) 185 | } 186 | 187 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 188 | let type_name = item.ident.to_string(); 189 | let mut inner; 190 | match &item.fields { 191 | Fields::Named(fields) => { 192 | inner = quote! { f.debug_struct(#type_name) }; 193 | let mut no_skips = true; 194 | for field in fields.named.iter() { 195 | let ident = field.ident.as_ref().unwrap(); 196 | if !args.ignore_named(ident) { 197 | let name = ident.to_string(); 198 | inner.append_all(quote! { 199 | .field(#name, &self.#ident) 200 | }); 201 | } else { 202 | no_skips = false; 203 | } 204 | } 205 | if no_skips { 206 | inner.append_all(quote! { .finish() }); 207 | } else { 208 | inner.append_all(quote! { .finish_non_exhaustive() }); 209 | }; 210 | } 211 | Fields::Unnamed(fields) => { 212 | inner = quote! { f.debug_tuple(#type_name) }; 213 | for i in 0..fields.unnamed.len() { 214 | let index = Index::from(i); 215 | if !args.ignore_unnamed(&index) { 216 | inner.append_all(quote! { 217 | .field(&self.#index) 218 | }); 219 | } else { 220 | inner.append_all(quote! { 221 | .field(&format_args!("_")) 222 | }); 223 | } 224 | } 225 | inner.append_all(quote! { .finish() }); 226 | } 227 | Fields::Unit => inner = quote! { f.write_str(#type_name) }, 228 | }; 229 | let method = quote! { 230 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 231 | #inner 232 | } 233 | }; 234 | Ok((quote! { ::core::fmt::Debug }, method)) 235 | } 236 | } 237 | 238 | /// Implement [`core::default::Default`] 239 | pub struct ImplDefault; 240 | impl ImplTrait for ImplDefault { 241 | fn path(&self) -> SimplePath { 242 | SimplePath::new(&["", "core", "default", "Default"]) 243 | } 244 | 245 | fn struct_items(&self, item: &ItemStruct, _: &ImplArgs) -> Result<(Toks, Toks)> { 246 | let type_ident = &item.ident; 247 | let mut inner; 248 | match &item.fields { 249 | Fields::Named(fields) => { 250 | inner = quote! {}; 251 | for field in fields.named.iter() { 252 | let ident = field.ident.as_ref().unwrap(); 253 | inner.append_all(quote! { #ident: Default::default(), }); 254 | } 255 | inner = quote! { #type_ident { #inner } }; 256 | } 257 | Fields::Unnamed(fields) => { 258 | inner = quote! {}; 259 | for _ in 0..fields.unnamed.len() { 260 | inner.append_all(quote! { Default::default(), }); 261 | } 262 | inner = quote! { #type_ident(#inner) }; 263 | } 264 | Fields::Unit => inner = quote! { #type_ident }, 265 | } 266 | let method = quote! { 267 | fn default() -> Self { 268 | #inner 269 | } 270 | }; 271 | Ok((quote! { ::core::default::Default }, method)) 272 | } 273 | } 274 | 275 | /// Implement [`core::cmp::PartialEq`] 276 | /// 277 | /// Restriction: `Rhs == Self` 278 | pub struct ImplPartialEq; 279 | impl ImplTrait for ImplPartialEq { 280 | fn path(&self) -> SimplePath { 281 | SimplePath::new(&["", "core", "cmp", "PartialEq"]) 282 | } 283 | 284 | fn support_ignore(&self) -> bool { 285 | true 286 | } 287 | 288 | fn enum_items(&self, item: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> { 289 | let mut idfmt = IdentFormatter::new(); 290 | let name = &item.ident; 291 | let mut variants = Toks::new(); 292 | for v in item.variants.iter() { 293 | let ident = &v.ident; 294 | let tag = quote! { #name :: #ident }; 295 | variants.append_all(match v.fields { 296 | Fields::Named(ref fields) => { 297 | let mut l_args = quote! {}; 298 | let mut r_args = quote! {}; 299 | let mut cond = quote! {}; 300 | for (i, field) in fields.named.iter().enumerate() { 301 | let ident = field.ident.as_ref().unwrap(); 302 | let li = idfmt.make_call_site(format_args!("__l{i}")); 303 | let ri = idfmt.make_call_site(format_args!("__r{i}")); 304 | l_args.append_all(quote! { #ident: #li, }); 305 | r_args.append_all(quote! { #ident: #ri, }); 306 | if !cond.is_empty() { 307 | cond.append_all(quote! { && }); 308 | } 309 | cond.append_all(quote! { #li == #ri }); 310 | } 311 | 312 | quote! { (#tag { #l_args }, #tag { #r_args }) => #cond, } 313 | } 314 | Fields::Unnamed(ref fields) => { 315 | let len = fields.unnamed.len(); 316 | let mut l_args = quote! {}; 317 | let mut r_args = quote! {}; 318 | let mut cond = quote! {}; 319 | for i in 0..len { 320 | let li = idfmt.make_call_site(format_args!("__l{i}")); 321 | let ri = idfmt.make_call_site(format_args!("__r{i}")); 322 | l_args.append_all(quote! { #li, }); 323 | r_args.append_all(quote! { #ri, }); 324 | if !cond.is_empty() { 325 | cond.append_all(quote! { && }); 326 | } 327 | cond.append_all(quote! { #li == #ri }); 328 | } 329 | 330 | quote! { (#tag ( #l_args ), #tag ( #r_args )) => #cond, } 331 | } 332 | Fields::Unit => quote! { (#tag, #tag) => true, }, 333 | }); 334 | } 335 | variants.append_all(quote! { (_, _) => false, }); 336 | 337 | let method = quote! { 338 | #[inline] 339 | fn eq(&self, other: &Self) -> bool { 340 | match (self, other) { 341 | #variants 342 | } 343 | } 344 | }; 345 | Ok((quote! { ::core::cmp::PartialEq }, method)) 346 | } 347 | 348 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 349 | let mut toks = Toks::new(); 350 | let mut require_sep = false; 351 | args.for_fields(&item.fields, |member: Member, _| { 352 | if require_sep { 353 | ::default().to_tokens(&mut toks); 354 | } 355 | toks.append_all(quote! { self.#member == other.#member }); 356 | require_sep = true; 357 | }); 358 | if toks.is_empty() { 359 | toks = quote! { true }; 360 | } 361 | 362 | let method = quote! { 363 | #[inline] 364 | fn eq(&self, other: &Self) -> bool { 365 | #toks 366 | } 367 | }; 368 | Ok((quote! { ::core::cmp::PartialEq }, method)) 369 | } 370 | } 371 | 372 | /// Implement [`core::cmp::Eq`] 373 | pub struct ImplEq; 374 | impl ImplTrait for ImplEq { 375 | fn path(&self) -> SimplePath { 376 | SimplePath::new(&["", "core", "cmp", "Eq"]) 377 | } 378 | 379 | fn allow_ignore_with(&self) -> Option { 380 | Some(SimplePath::new(&["", "core", "cmp", "PartialEq"])) 381 | } 382 | 383 | fn enum_items(&self, _: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> { 384 | Ok((quote! { ::core::cmp::Eq }, quote! {})) 385 | } 386 | 387 | fn struct_items(&self, _: &ItemStruct, _: &ImplArgs) -> Result<(Toks, Toks)> { 388 | Ok((quote! { ::core::cmp::Eq }, quote! {})) 389 | } 390 | } 391 | 392 | /// Implement [`core::cmp::PartialOrd`] 393 | /// 394 | /// Restriction: `Rhs == Self` 395 | pub struct ImplPartialOrd; 396 | impl ImplTrait for ImplPartialOrd { 397 | fn path(&self) -> SimplePath { 398 | SimplePath::new(&["", "core", "cmp", "PartialOrd"]) 399 | } 400 | 401 | fn support_ignore(&self) -> bool { 402 | true 403 | } 404 | 405 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 406 | let mut toks = Toks::new(); 407 | args.for_fields_iter(item.fields.iter().enumerate().rev(), |member: Member, _| { 408 | let cmp = 409 | quote! { ::core::cmp::PartialOrd::partial_cmp(&self.#member, &other.#member) }; 410 | if toks.is_empty() { 411 | toks = cmp; 412 | } else { 413 | toks = quote! { 414 | match #cmp { 415 | ::core::option::Option::Some(::core::cmp::Ordering::Equal) => #toks, 416 | cmp => cmp, 417 | } 418 | } 419 | } 420 | }); 421 | if toks.is_empty() { 422 | toks = quote! { ::core::option::Option::Some(::core::cmp::Ordering::Equal) }; 423 | } 424 | 425 | let method = quote! { 426 | #[inline] 427 | fn partial_cmp(&self, other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { 428 | #toks 429 | } 430 | }; 431 | Ok((quote! { ::core::cmp::PartialOrd }, method)) 432 | } 433 | } 434 | 435 | /// Implement [`core::cmp::Ord`] 436 | pub struct ImplOrd; 437 | impl ImplTrait for ImplOrd { 438 | fn path(&self) -> SimplePath { 439 | SimplePath::new(&["", "core", "cmp", "Ord"]) 440 | } 441 | 442 | fn support_ignore(&self) -> bool { 443 | true 444 | } 445 | 446 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 447 | let mut toks = Toks::new(); 448 | args.for_fields_iter(item.fields.iter().enumerate().rev(), |member: Member, _| { 449 | let cmp = quote! { ::core::cmp::Ord::cmp(&self.#member, &other.#member) }; 450 | if toks.is_empty() { 451 | toks = cmp; 452 | } else { 453 | toks = quote! { 454 | match #cmp { 455 | ::core::cmp::Ordering::Equal => #toks, 456 | cmp => cmp, 457 | } 458 | } 459 | } 460 | }); 461 | if toks.is_empty() { 462 | toks = quote! { ::core::cmp::Ordering::Equal }; 463 | } 464 | 465 | let method = quote! { 466 | #[inline] 467 | fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { 468 | #toks 469 | } 470 | }; 471 | Ok((quote! { ::core::cmp::Ord }, method)) 472 | } 473 | } 474 | 475 | /// Implement [`core::hash::Hash`] 476 | pub struct ImplHash; 477 | impl ImplTrait for ImplHash { 478 | fn path(&self) -> SimplePath { 479 | SimplePath::new(&["", "core", "hash", "Hash"]) 480 | } 481 | 482 | fn support_ignore(&self) -> bool { 483 | true 484 | } 485 | 486 | fn enum_items(&self, item: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> { 487 | let mut idfmt = IdentFormatter::new(); 488 | let name = &item.ident; 489 | let mut variants = Toks::new(); 490 | for v in item.variants.iter() { 491 | let ident = &v.ident; 492 | variants.append_all(quote! { #name :: #ident }); 493 | variants.append_all(match v.fields { 494 | Fields::Named(ref fields) => { 495 | let idents = fields.named.iter().map(|f| f.ident.as_ref().unwrap()); 496 | let hashes = fields.named.iter().map(|f| { 497 | let ident = f.ident.as_ref().unwrap(); 498 | quote! { ::core::hash::Hash::hash(&#ident, state); } 499 | }); 500 | quote! { { #(#idents),* } => { #(#hashes);* } } 501 | } 502 | Fields::Unnamed(ref fields) => { 503 | let len = fields.unnamed.len(); 504 | let mut bindings = Vec::with_capacity(len); 505 | let mut hashes = quote! {}; 506 | for i in 0..len { 507 | let ident = idfmt.make_call_site(format_args!("_{i}")); 508 | bindings.push(quote! { ref #ident }); 509 | hashes.append_all(quote! { 510 | ::core::hash::Hash::hash(&#ident, state); 511 | }); 512 | } 513 | quote! { ( #(#bindings),* ) => { #hashes } } 514 | } 515 | Fields::Unit => quote! { => (), }, 516 | }); 517 | } 518 | let method = quote! { 519 | fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) { 520 | match *self { 521 | #variants 522 | } 523 | } 524 | }; 525 | Ok((quote! { ::core::hash::Hash }, method)) 526 | } 527 | 528 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 529 | let mut toks = Toks::new(); 530 | args.for_fields_iter(item.fields.iter().enumerate().rev(), |member: Member, _| { 531 | toks.append_all(quote! { ::core::hash::Hash::hash(&self.#member, state); }); 532 | }); 533 | 534 | let method = quote! { 535 | #[inline] 536 | fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) { 537 | #toks 538 | } 539 | }; 540 | Ok((quote! { ::core::hash::Hash }, method)) 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /lib/src/autoimpl/impl_using.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! Impls "using" a field 7 | 8 | use super::{Error, ImplArgs, ImplTrait, Result}; 9 | use crate::SimplePath; 10 | use proc_macro2::TokenStream as Toks; 11 | use quote::quote; 12 | use syn::{ItemStruct, PathArguments}; 13 | 14 | /// Implement [`core::borrow::Borrow`] 15 | pub struct ImplBorrow; 16 | impl ImplTrait for ImplBorrow { 17 | fn path(&self) -> SimplePath { 18 | SimplePath::new(&["", "core", "borrow", "Borrow"]) 19 | } 20 | 21 | fn support_using(&self) -> bool { 22 | true 23 | } 24 | 25 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 26 | if let Some(field) = args.using_field(&item.fields) { 27 | let ty = field.ty.clone(); 28 | let member = args.using_member().unwrap(); 29 | let method = quote! { 30 | fn borrow(&self) -> & #ty { 31 | &self.#member 32 | } 33 | }; 34 | Ok((quote! { ::core::borrow::Borrow<#ty> }, method)) 35 | } else { 36 | Err(Error::RequireUsing) 37 | } 38 | } 39 | } 40 | 41 | /// Implement [`core::borrow::BorrowMut`] 42 | pub struct ImplBorrowMut; 43 | impl ImplTrait for ImplBorrowMut { 44 | fn path(&self) -> SimplePath { 45 | SimplePath::new(&["", "core", "borrow", "BorrowMut"]) 46 | } 47 | 48 | fn support_using(&self) -> bool { 49 | true 50 | } 51 | 52 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 53 | if let Some(field) = args.using_field(&item.fields) { 54 | let ty = field.ty.clone(); 55 | let member = args.using_member().unwrap(); 56 | let method = quote! { 57 | fn borrow_mut(&mut self) -> &mut #ty { 58 | &mut self.#member 59 | } 60 | }; 61 | Ok((quote! { ::core::borrow::BorrowMut<#ty> }, method)) 62 | } else { 63 | Err(Error::RequireUsing) 64 | } 65 | } 66 | } 67 | 68 | /// Implement [`core::convert::AsRef`] 69 | pub struct ImplAsRef; 70 | impl ImplTrait for ImplAsRef { 71 | fn path(&self) -> SimplePath { 72 | SimplePath::new(&["", "core", "convert", "AsRef"]) 73 | } 74 | 75 | fn support_using(&self) -> bool { 76 | true 77 | } 78 | 79 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 80 | if let Some(field) = args.using_field(&item.fields) { 81 | let ty = field.ty.clone(); 82 | let member = args.using_member().unwrap(); 83 | let method = quote! { 84 | fn as_ref(&self) -> & #ty { 85 | &self.#member 86 | } 87 | }; 88 | Ok((quote! { ::core::convert::AsRef<#ty> }, method)) 89 | } else { 90 | Err(Error::RequireUsing) 91 | } 92 | } 93 | } 94 | 95 | /// Implement [`core::convert::AsMut`] 96 | pub struct ImplAsMut; 97 | impl ImplTrait for ImplAsMut { 98 | fn path(&self) -> SimplePath { 99 | SimplePath::new(&["", "core", "convert", "AsMut"]) 100 | } 101 | 102 | fn support_using(&self) -> bool { 103 | true 104 | } 105 | 106 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 107 | if let Some(field) = args.using_field(&item.fields) { 108 | let ty = field.ty.clone(); 109 | let member = args.using_member().unwrap(); 110 | let method = quote! { 111 | fn as_mut(&mut self) -> &mut #ty { 112 | &mut self.#member 113 | } 114 | }; 115 | Ok((quote! { ::core::convert::AsMut<#ty> }, method)) 116 | } else { 117 | Err(Error::RequireUsing) 118 | } 119 | } 120 | } 121 | 122 | /// Implement [`core::ops::Deref`] 123 | pub struct ImplDeref; 124 | impl ImplTrait for ImplDeref { 125 | fn path(&self) -> SimplePath { 126 | SimplePath::new(&["", "core", "ops", "Deref"]) 127 | } 128 | 129 | fn support_path_arguments(&self) -> bool { 130 | true 131 | } 132 | 133 | fn support_using(&self) -> bool { 134 | true 135 | } 136 | 137 | fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 138 | if let Some(field) = args.using_field(&item.fields) { 139 | let target = match args.path_arguments { 140 | PathArguments::None => field.ty.clone(), 141 | PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { 142 | ref args, 143 | .. 144 | }) => { 145 | let mut result = None; 146 | for arg in args { 147 | if let syn::GenericArgument::AssocType(b) = arg { 148 | if b.ident == "Target" && result.is_none() { 149 | result = Some(b.ty.clone()); 150 | continue; 151 | } 152 | } 153 | return Err(Error::PathArguments("expected ``")); 154 | } 155 | match result { 156 | Some(r) => r, 157 | None => return Err(Error::PathArguments("expected ``")), 158 | } 159 | } 160 | PathArguments::Parenthesized(_) => return Err(Error::PathArguments("unexpected")), 161 | }; 162 | 163 | let member = args.using_member().unwrap(); 164 | let method = quote! { 165 | type Target = #target; 166 | fn deref(&self) -> &Self::Target { 167 | &self.#member 168 | } 169 | }; 170 | Ok((quote! { ::core::ops::Deref }, method)) 171 | } else { 172 | Err(Error::RequireUsing) 173 | } 174 | } 175 | } 176 | 177 | /// Implement [`core::ops::DerefMut`] 178 | pub struct ImplDerefMut; 179 | impl ImplTrait for ImplDerefMut { 180 | fn path(&self) -> SimplePath { 181 | SimplePath::new(&["", "core", "ops", "DerefMut"]) 182 | } 183 | 184 | fn support_using(&self) -> bool { 185 | true 186 | } 187 | 188 | fn struct_items(&self, _: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> { 189 | if let Some(member) = args.using_member() { 190 | let method = quote! { 191 | fn deref_mut(&mut self) -> &mut Self::Target { 192 | &mut self.#member 193 | } 194 | }; 195 | Ok((quote! { ::core::ops::DerefMut }, method)) 196 | } else { 197 | Err(Error::RequireUsing) 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /lib/src/default.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | use crate::fields::{Fields, FieldsNamed, FieldsUnnamed}; 7 | use crate::generics::{clause_to_toks, WhereClause}; 8 | use crate::scope::{Scope, ScopeAttr, ScopeItem}; 9 | use crate::SimplePath; 10 | use proc_macro2::{Span, TokenStream}; 11 | use proc_macro_error2::emit_error; 12 | use quote::quote; 13 | use syn::parse::{Error, Parse, ParseStream, Result}; 14 | use syn::spanned::Spanned; 15 | use syn::{parse2, Attribute, Expr, Generics, Ident, Item, Meta, Token}; 16 | 17 | /// `#[impl_default]` attribute 18 | pub struct ImplDefault { 19 | expr: Option, 20 | where_clause: Option, 21 | span: Span, 22 | } 23 | 24 | impl ImplDefault { 25 | /// Expand over the given `item` 26 | /// 27 | /// This attribute (in this form of invocation) does not modify the item. 28 | /// The caller should append the result to `item` tokens. 29 | pub fn expand(self, item: TokenStream) -> TokenStream { 30 | let attr_span = self.span; 31 | if self.expr.is_some() { 32 | let item = match parse2::(item) { 33 | Ok(item) => item, 34 | Err(err) => { 35 | emit_error!(err.span(), "{}", err); 36 | return TokenStream::new(); 37 | } 38 | }; 39 | 40 | match item { 41 | Item::Enum(syn::ItemEnum { 42 | ident, generics, .. 43 | }) 44 | | Item::Struct(syn::ItemStruct { 45 | ident, generics, .. 46 | }) 47 | | Item::Type(syn::ItemType { 48 | ident, generics, .. 49 | }) 50 | | Item::Union(syn::ItemUnion { 51 | ident, generics, .. 52 | }) => self.gen_expr(&ident, &generics), 53 | item => { 54 | emit_error!( 55 | item, 56 | "default: only supports enum, struct, type alias and union items" 57 | ); 58 | TokenStream::new() 59 | } 60 | } 61 | } else { 62 | emit_error!(attr_span, "invalid use outside of `impl_scope!` macro"); 63 | TokenStream::new() 64 | } 65 | } 66 | 67 | fn gen_expr(self, ident: &Ident, generics: &Generics) -> TokenStream { 68 | let (impl_generics, ty_generics, _) = generics.split_for_impl(); 69 | let wc = clause_to_toks( 70 | &self.where_clause, 71 | generics.where_clause.as_ref(), 72 | "e! { Default }, 73 | ); 74 | let expr = self.expr.unwrap(); 75 | 76 | quote! { 77 | #[automatically_derived] 78 | impl #impl_generics core::default::Default for #ident #ty_generics #wc { 79 | fn default() -> Self { 80 | #expr 81 | } 82 | } 83 | } 84 | } 85 | 86 | fn parse_attr(attr: Attribute) -> Result { 87 | match attr.meta { 88 | Meta::Path(_) => Ok(ImplDefault { 89 | expr: None, 90 | where_clause: None, 91 | span: attr.span(), 92 | }), 93 | Meta::List(list) => list.parse_args(), 94 | Meta::NameValue(meta) => Err(Error::new_spanned( 95 | meta, 96 | "expected #[impl_default] or #[impl_default(EXPR)]", 97 | )), 98 | } 99 | } 100 | } 101 | 102 | /// [`ScopeAttr`] rule enabling `#[impl_default]` within `impl_scope!` 103 | pub struct AttrImplDefault; 104 | impl ScopeAttr for AttrImplDefault { 105 | fn path(&self) -> SimplePath { 106 | SimplePath(&["impl_default"]) 107 | } 108 | 109 | fn apply(&self, attr: Attribute, scope: &mut Scope) -> Result<()> { 110 | let args = ImplDefault::parse_attr(attr)?; 111 | 112 | if args.expr.is_some() { 113 | scope 114 | .generated 115 | .push(args.gen_expr(&scope.ident, &scope.generics)); 116 | } else { 117 | let fields = match &mut scope.item { 118 | ScopeItem::Struct { fields, .. } => match fields { 119 | Fields::Named(FieldsNamed { fields, .. }) 120 | | Fields::Unnamed(FieldsUnnamed { fields, .. }) => { 121 | let iter = fields.iter_mut().map(|field| { 122 | let ident = &field.ident; 123 | if let Some(expr) = field.assign.take().map(|a| a.1) { 124 | quote! { #ident : #expr } 125 | } else { 126 | quote! { #ident : Default::default() } 127 | } 128 | }); 129 | quote! { #(#iter),* } 130 | } 131 | Fields::Unit => quote! {}, 132 | }, 133 | _ => { 134 | return Err(Error::new( 135 | args.span, 136 | "must specify value as `#[impl_default(value)]` on non-struct type", 137 | )); 138 | } 139 | }; 140 | 141 | let ident = &scope.ident; 142 | let (impl_generics, ty_generics, _) = scope.generics.split_for_impl(); 143 | let wc = clause_to_toks( 144 | &args.where_clause, 145 | scope.generics.where_clause.as_ref(), 146 | "e! { Default }, 147 | ); 148 | 149 | scope.generated.push(quote! { 150 | #[automatically_derived] 151 | impl #impl_generics core::default::Default for #ident #ty_generics #wc { 152 | fn default() -> Self { 153 | #ident { 154 | #fields 155 | } 156 | } 157 | } 158 | }); 159 | } 160 | Ok(()) 161 | } 162 | } 163 | 164 | impl Parse for ImplDefault { 165 | fn parse(input: ParseStream) -> Result { 166 | let mut expr = None; 167 | let mut where_clause = None; 168 | let span = input.span(); 169 | 170 | if !input.peek(Token![where]) && !input.is_empty() { 171 | expr = Some(input.parse()?); 172 | } 173 | 174 | if input.peek(Token![where]) { 175 | where_clause = Some(input.parse()?); 176 | } 177 | 178 | if !input.is_empty() { 179 | return Err(Error::new(input.span(), "unexpected")); 180 | } 181 | 182 | Ok(ImplDefault { 183 | expr, 184 | where_clause, 185 | span, 186 | }) 187 | } 188 | } 189 | 190 | /// Helper fn which can be passed to [`Scope::apply_attrs`] 191 | /// 192 | /// This optionally matches [`AttrImplDefault`]. 193 | pub fn find_impl_default(path: &syn::Path) -> Option<&'static dyn ScopeAttr> { 194 | AttrImplDefault 195 | .path() 196 | .matches(path) 197 | .then(|| &AttrImplDefault as &dyn ScopeAttr) 198 | } 199 | -------------------------------------------------------------------------------- /lib/src/fields.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! Custom version of [`syn`] fields types supporting initializers 7 | 8 | use proc_macro2::TokenStream; 9 | use proc_macro_error2::emit_error; 10 | use quote::{ToTokens, TokenStreamExt}; 11 | use syn::parse::{Parse, ParseStream, Result}; 12 | use syn::punctuated::Punctuated; 13 | use syn::{token, Attribute, Expr, Ident, Token, Type, Visibility}; 14 | 15 | /// Struct style: unit/tuple/regular 16 | #[derive(Debug)] 17 | pub enum StructStyle { 18 | /// A unit struct (e.g. `struct Foo;`) 19 | Unit(Token![;]), 20 | /// A tuple struct (e.g. `struct Foo(f32, f32);`) 21 | Tuple(token::Paren, Token![;]), 22 | /// A regular struct (e.g. `struct Foo { x: f32, y: f32 }`) 23 | Regular(token::Brace), 24 | } 25 | 26 | /// Data stored within an enum variant or struct. 27 | /// 28 | /// This is a variant of [`syn::Fields`] supporting field initializers. 29 | #[derive(Debug)] 30 | pub enum Fields { 31 | /// Named fields of a struct or struct variant such as `Point { x: f64, y: f64 }`. 32 | Named(FieldsNamed), 33 | /// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`. 34 | Unnamed(FieldsUnnamed), 35 | /// Unit struct or unit variant such as `None`. 36 | Unit, 37 | } 38 | 39 | /// Named fields of a struct or struct variant such as `Point { x: f64, y: f64 }`. 40 | /// 41 | /// This is a variant of [`syn::FieldsNamed`] supporting field initializers. 42 | #[derive(Debug)] 43 | pub struct FieldsNamed { 44 | /// `{ ... }` around fields 45 | pub brace_token: token::Brace, 46 | /// Fields 47 | pub fields: Punctuated, 48 | } 49 | 50 | /// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`. 51 | /// 52 | /// This is a variant of [`syn::FieldsUnnamed`] supporting field initializers. 53 | #[derive(Debug)] 54 | pub struct FieldsUnnamed { 55 | /// `( ... )` around fields 56 | pub paren_token: token::Paren, 57 | /// Fields 58 | pub fields: Punctuated, 59 | } 60 | 61 | /// A field of a struct or enum variant. 62 | /// 63 | /// This is a variant of [`syn::Field`] supporting field initializers. 64 | #[derive(Debug)] 65 | pub struct Field { 66 | /// Attributes tagged on the field. 67 | pub attrs: Vec, 68 | /// Visibility of the field. 69 | pub vis: Visibility, 70 | /// Name of the field, if any. 71 | pub ident: Option, 72 | /// `:` token before type 73 | pub colon_token: Option, 74 | /// Type of the field. 75 | pub ty: Type, 76 | /// Optional field initializer. 77 | /// 78 | /// This is considered legal input when parsing, but not legal output. An 79 | /// attribute rule such as [`AttrImplDefault`](crate::scope::AttrImplDefault) 80 | /// must remove the initializer before output is generated. 81 | pub assign: Option<(Token![=], Expr)>, 82 | } 83 | 84 | // Copied from syn, modified 85 | pub(crate) mod parsing { 86 | use super::*; 87 | use syn::ext::IdentExt; 88 | use syn::{braced, parenthesized, WhereClause}; 89 | 90 | impl Parse for FieldsNamed { 91 | fn parse(input: ParseStream) -> Result { 92 | let content; 93 | let brace_token = braced!(content in input); 94 | Ok(FieldsNamed { 95 | brace_token, 96 | fields: content.parse_terminated(Field::parse_named, Token![,])?, 97 | }) 98 | } 99 | } 100 | 101 | impl Parse for FieldsUnnamed { 102 | fn parse(input: ParseStream) -> Result { 103 | let content; 104 | let paren_token = parenthesized!(content in input); 105 | Ok(FieldsUnnamed { 106 | paren_token, 107 | fields: content.parse_terminated(Field::parse_unnamed, Token![,])?, 108 | }) 109 | } 110 | } 111 | 112 | impl Field { 113 | /// Parse a named field 114 | pub fn parse_named(input: ParseStream) -> Result { 115 | Ok(Field { 116 | attrs: input.call(Attribute::parse_outer)?, 117 | vis: input.parse()?, 118 | ident: Some(if input.peek(Token![_]) { 119 | input.call(Ident::parse_any) 120 | } else { 121 | input.parse() 122 | }?), 123 | colon_token: Some(input.parse()?), 124 | ty: input.parse()?, 125 | assign: if input.peek(Token![=]) { 126 | Some((input.parse()?, input.parse()?)) 127 | } else { 128 | None 129 | }, 130 | }) 131 | } 132 | 133 | /// Parse an unnamed field 134 | pub fn parse_unnamed(input: ParseStream) -> Result { 135 | Ok(Field { 136 | attrs: input.call(Attribute::parse_outer)?, 137 | vis: input.parse()?, 138 | ident: None, 139 | colon_token: None, 140 | ty: input.parse()?, 141 | assign: if input.peek(Token![=]) { 142 | Some((input.parse()?, input.parse()?)) 143 | } else { 144 | None 145 | }, 146 | }) 147 | } 148 | } 149 | 150 | pub(crate) fn data_struct( 151 | input: ParseStream, 152 | ) -> Result<(Option, Fields, Option)> { 153 | let mut lookahead = input.lookahead1(); 154 | let mut where_clause = None; 155 | if lookahead.peek(Token![where]) { 156 | where_clause = Some(input.parse()?); 157 | lookahead = input.lookahead1(); 158 | } 159 | 160 | if where_clause.is_none() && lookahead.peek(token::Paren) { 161 | let fields = input.parse()?; 162 | 163 | lookahead = input.lookahead1(); 164 | if lookahead.peek(Token![where]) { 165 | where_clause = Some(input.parse()?); 166 | lookahead = input.lookahead1(); 167 | } 168 | 169 | if lookahead.peek(Token![;]) { 170 | let semi = input.parse()?; 171 | Ok((where_clause, Fields::Unnamed(fields), Some(semi))) 172 | } else { 173 | Err(lookahead.error()) 174 | } 175 | } else if lookahead.peek(token::Brace) { 176 | let fields = parse_braced(input)?; 177 | Ok((where_clause, Fields::Named(fields), None)) 178 | } else if lookahead.peek(Token![;]) { 179 | let semi = input.parse()?; 180 | Ok((where_clause, Fields::Unit, Some(semi))) 181 | } else { 182 | Err(lookahead.error()) 183 | } 184 | } 185 | 186 | fn parse_braced(input: ParseStream) -> Result { 187 | let content; 188 | let brace_token = braced!(content in input); 189 | let fields = content.parse_terminated(Field::parse_named, Token![,])?; 190 | Ok(FieldsNamed { 191 | brace_token, 192 | fields, 193 | }) 194 | } 195 | } 196 | 197 | mod printing { 198 | use super::*; 199 | 200 | impl ToTokens for FieldsNamed { 201 | fn to_tokens(&self, tokens: &mut TokenStream) { 202 | self.brace_token.surround(tokens, |tokens| { 203 | self.fields.to_tokens(tokens); 204 | }); 205 | } 206 | } 207 | 208 | impl ToTokens for FieldsUnnamed { 209 | fn to_tokens(&self, tokens: &mut TokenStream) { 210 | self.paren_token.surround(tokens, |tokens| { 211 | self.fields.to_tokens(tokens); 212 | }); 213 | } 214 | } 215 | 216 | impl ToTokens for Field { 217 | fn to_tokens(&self, tokens: &mut TokenStream) { 218 | tokens.append_all(&self.attrs); 219 | self.vis.to_tokens(tokens); 220 | if let Some(ident) = &self.ident { 221 | ident.to_tokens(tokens); 222 | self.colon_token.unwrap_or_default().to_tokens(tokens); 223 | } 224 | self.ty.to_tokens(tokens); 225 | 226 | if let Some(ref assign) = self.assign { 227 | emit_error!( 228 | assign.0, 229 | "default value on `struct` field in output"; 230 | help = "did you mean to use the `#[impl_default]` attribute?", 231 | ); 232 | } 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /lib/src/generics.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! Custom version of [`syn`] generics supporting 'X: trait' bound 7 | 8 | use proc_macro2::TokenStream; 9 | use quote::{quote, ToTokens, TokenStreamExt}; 10 | use syn::parse::{Parse, ParseStream, Result}; 11 | use syn::punctuated::{Pair, Punctuated}; 12 | use syn::token; 13 | use syn::{Attribute, ConstParam, LifetimeParam, PredicateLifetime}; 14 | use syn::{BoundLifetimes, Ident, Lifetime, Token, Type}; 15 | 16 | /// Lifetimes and type parameters attached an item 17 | /// 18 | /// This is a custom variant of [`syn::Generics`] 19 | /// which supports `trait` as a parameter bound. 20 | #[derive(Debug)] 21 | pub struct Generics { 22 | /// `<` 23 | pub lt_token: Option, 24 | /// Parameters 25 | pub params: Punctuated, 26 | /// `>` 27 | pub gt_token: Option]>, 28 | /// `where` bounds 29 | pub where_clause: Option, 30 | } 31 | 32 | impl Default for Generics { 33 | fn default() -> Self { 34 | Generics { 35 | lt_token: None, 36 | params: Punctuated::new(), 37 | gt_token: None, 38 | where_clause: None, 39 | } 40 | } 41 | } 42 | 43 | /// A generic type parameter, lifetime, or const generic 44 | /// 45 | /// This is a custom variant of [`syn::GenericParam`] 46 | /// which supports `trait` as a parameter bound. 47 | #[derive(Debug)] 48 | #[allow(clippy::large_enum_variant)] 49 | pub enum GenericParam { 50 | /// Type parameter 51 | Type(TypeParam), 52 | /// Lifetime parameter 53 | Lifetime(LifetimeParam), 54 | /// `const` parameter 55 | Const(ConstParam), 56 | } 57 | 58 | /// A generic type parameter: `T: Into`. 59 | /// 60 | /// This is a custom variant of [`syn::TypeParam`] 61 | /// which supports `trait` as a parameter bound. 62 | #[derive(Debug)] 63 | pub struct TypeParam { 64 | /// Type parameter attributes 65 | pub attrs: Vec, 66 | /// Type parameter identifier 67 | pub ident: Ident, 68 | /// `:` 69 | pub colon_token: Option, 70 | /// List of type bounds 71 | pub bounds: Punctuated, 72 | /// `=` 73 | pub eq_token: Option, 74 | /// Optional default value 75 | pub default: Option, 76 | } 77 | 78 | /// A trait or lifetime used as a bound on a type parameter. 79 | /// 80 | /// This is a superset of [`syn::TypeParamBound`]. 81 | #[derive(Debug)] 82 | pub enum TypeParamBound { 83 | /// `trait` used as a bound (substituted for the trait name by [`ToTokensSubst`]) 84 | TraitSubst(Token![trait]), 85 | /// Everything else 86 | Other(syn::TypeParamBound), 87 | } 88 | 89 | /// A `where` clause in a definition: `where T: Deserialize<'de>, D: 'static`. 90 | /// 91 | /// This is a custom variant of [`syn::WhereClause`] 92 | /// which supports `trait` as a parameter bound. 93 | #[derive(Debug)] 94 | pub struct WhereClause { 95 | /// `where` 96 | pub where_token: Token![where], 97 | /// Parameter bounds 98 | pub predicates: Punctuated, 99 | } 100 | 101 | /// A single predicate in a `where` clause: `T: Deserialize<'de>`. 102 | /// 103 | /// This is a custom variant of [`syn::WherePredicate`] 104 | /// which supports `trait` as a parameter bound. 105 | #[allow(clippy::large_enum_variant)] 106 | #[derive(Debug)] 107 | pub enum WherePredicate { 108 | /// A type predicate in a `where` clause: `for<'c> Foo<'c>: Trait<'c>`. 109 | Type(PredicateType), 110 | 111 | /// A lifetime predicate in a `where` clause: `'a: 'b + 'c`. 112 | Lifetime(PredicateLifetime), 113 | } 114 | 115 | /// A type predicate in a `where` clause: `for<'c> Foo<'c>: Trait<'c>`. 116 | /// 117 | /// This is a custom variant of [`syn::PredicateType`] 118 | /// which supports `trait` as a parameter bound. 119 | #[derive(Debug)] 120 | pub struct PredicateType { 121 | /// Any lifetimes from a `for` binding 122 | pub lifetimes: Option, 123 | /// The type being bounded 124 | pub bounded_ty: Type, 125 | /// `:` before bounds 126 | pub colon_token: Token![:], 127 | /// Trait and lifetime bounds (`Clone+Send+'static`) 128 | pub bounds: Punctuated, 129 | } 130 | 131 | mod parsing { 132 | use super::*; 133 | use syn::ext::IdentExt; 134 | 135 | impl Parse for Generics { 136 | fn parse(input: ParseStream) -> Result { 137 | if !input.peek(Token![<]) { 138 | return Ok(Generics::default()); 139 | } 140 | 141 | let lt_token: Token![<] = input.parse()?; 142 | 143 | let mut params = Punctuated::new(); 144 | loop { 145 | if input.peek(Token![>]) { 146 | break; 147 | } 148 | 149 | let attrs = input.call(Attribute::parse_outer)?; 150 | let lookahead = input.lookahead1(); 151 | if lookahead.peek(Lifetime) { 152 | params.push_value(GenericParam::Lifetime(LifetimeParam { 153 | attrs, 154 | ..input.parse()? 155 | })); 156 | } else if lookahead.peek(Ident) { 157 | params.push_value(GenericParam::Type(TypeParam { 158 | attrs, 159 | ..input.parse()? 160 | })); 161 | } else if lookahead.peek(Token![const]) { 162 | params.push_value(GenericParam::Const(ConstParam { 163 | attrs, 164 | ..input.parse()? 165 | })); 166 | } else if input.peek(Token![_]) { 167 | params.push_value(GenericParam::Type(TypeParam { 168 | attrs, 169 | ident: input.call(Ident::parse_any)?, 170 | colon_token: None, 171 | bounds: Punctuated::new(), 172 | eq_token: None, 173 | default: None, 174 | })); 175 | } else { 176 | return Err(lookahead.error()); 177 | } 178 | 179 | if input.peek(Token![>]) { 180 | break; 181 | } 182 | let punct = input.parse()?; 183 | params.push_punct(punct); 184 | } 185 | 186 | let gt_token: Token![>] = input.parse()?; 187 | 188 | Ok(Generics { 189 | lt_token: Some(lt_token), 190 | params, 191 | gt_token: Some(gt_token), 192 | where_clause: None, 193 | }) 194 | } 195 | } 196 | 197 | impl Parse for TypeParam { 198 | fn parse(input: ParseStream) -> Result { 199 | let attrs = input.call(Attribute::parse_outer)?; 200 | let ident: Ident = input.parse()?; 201 | let colon_token: Option = input.parse()?; 202 | 203 | let mut bounds = Punctuated::new(); 204 | if colon_token.is_some() { 205 | loop { 206 | if input.peek(Token![,]) || input.peek(Token![>]) || input.peek(Token![=]) { 207 | break; 208 | } 209 | let value: TypeParamBound = input.parse()?; 210 | bounds.push_value(value); 211 | if !input.peek(Token![+]) { 212 | break; 213 | } 214 | let punct: Token![+] = input.parse()?; 215 | bounds.push_punct(punct); 216 | } 217 | } 218 | 219 | let eq_token: Option = input.parse()?; 220 | let default = if eq_token.is_some() { 221 | Some(input.parse::()?) 222 | } else { 223 | None 224 | }; 225 | 226 | Ok(TypeParam { 227 | attrs, 228 | ident, 229 | colon_token, 230 | bounds, 231 | eq_token, 232 | default, 233 | }) 234 | } 235 | } 236 | 237 | impl Parse for TypeParamBound { 238 | fn parse(input: ParseStream) -> Result { 239 | if input.peek(Token![trait]) { 240 | input.parse().map(TypeParamBound::TraitSubst) 241 | } else { 242 | syn::TypeParamBound::parse(input).map(TypeParamBound::Other) 243 | } 244 | } 245 | } 246 | 247 | impl Parse for WhereClause { 248 | fn parse(input: ParseStream) -> Result { 249 | Ok(WhereClause { 250 | where_token: input.parse()?, 251 | predicates: { 252 | let mut predicates = Punctuated::new(); 253 | loop { 254 | if input.is_empty() 255 | || input.peek(token::Brace) 256 | || input.peek(Token![,]) 257 | || input.peek(Token![;]) 258 | || input.peek(Token![:]) && !input.peek(Token![::]) 259 | || input.peek(Token![=]) 260 | { 261 | break; 262 | } 263 | let value = input.parse()?; 264 | predicates.push_value(value); 265 | if !input.peek(Token![,]) { 266 | break; 267 | } 268 | let punct = input.parse()?; 269 | predicates.push_punct(punct); 270 | } 271 | predicates 272 | }, 273 | }) 274 | } 275 | } 276 | 277 | impl Parse for WherePredicate { 278 | fn parse(input: ParseStream) -> Result { 279 | if input.peek(Lifetime) && input.peek2(Token![:]) { 280 | Ok(WherePredicate::Lifetime(PredicateLifetime { 281 | lifetime: input.parse()?, 282 | colon_token: input.parse()?, 283 | bounds: { 284 | let mut bounds = Punctuated::new(); 285 | loop { 286 | if input.is_empty() 287 | || input.peek(token::Brace) 288 | || input.peek(Token![,]) 289 | || input.peek(Token![;]) 290 | || input.peek(Token![:]) 291 | || input.peek(Token![=]) 292 | { 293 | break; 294 | } 295 | let value = input.parse()?; 296 | bounds.push_value(value); 297 | if !input.peek(Token![+]) { 298 | break; 299 | } 300 | let punct = input.parse()?; 301 | bounds.push_punct(punct); 302 | } 303 | bounds 304 | }, 305 | })) 306 | } else { 307 | Ok(WherePredicate::Type(PredicateType { 308 | lifetimes: input.parse()?, 309 | bounded_ty: input.parse()?, 310 | colon_token: input.parse()?, 311 | bounds: { 312 | let mut bounds = Punctuated::new(); 313 | loop { 314 | if input.is_empty() 315 | || input.peek(token::Brace) 316 | || input.peek(Token![,]) 317 | || input.peek(Token![;]) 318 | || input.peek(Token![:]) && !input.peek(Token![::]) 319 | || input.peek(Token![=]) 320 | { 321 | break; 322 | } 323 | let value = input.parse()?; 324 | bounds.push_value(value); 325 | if !input.peek(Token![+]) { 326 | break; 327 | } 328 | let punct = input.parse()?; 329 | bounds.push_punct(punct); 330 | } 331 | bounds 332 | }, 333 | })) 334 | } 335 | } 336 | } 337 | } 338 | 339 | /// Tokenization trait with substitution 340 | /// 341 | /// This is similar to [`quote::ToTokens`], but replaces instances of `trait` 342 | /// as a parameter bound with `subst`. 343 | pub trait ToTokensSubst { 344 | /// Write `self` to the given [`TokenStream`] 345 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream); 346 | } 347 | 348 | mod printing_subst { 349 | use super::*; 350 | use syn::AttrStyle; 351 | 352 | impl ToTokensSubst for Generics { 353 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 354 | if self.params.is_empty() { 355 | return; 356 | } 357 | 358 | self.lt_token.unwrap_or_default().to_tokens(tokens); 359 | 360 | let mut trailing_or_empty = true; 361 | for pair in self.params.pairs() { 362 | if let GenericParam::Lifetime(param) = *pair.value() { 363 | param.to_tokens(tokens); 364 | pair.punct().to_tokens(tokens); 365 | trailing_or_empty = pair.punct().is_some(); 366 | } 367 | } 368 | for pair in self.params.pairs() { 369 | match *pair.value() { 370 | GenericParam::Type(param) => { 371 | if !trailing_or_empty { 372 | ::default().to_tokens(tokens); 373 | trailing_or_empty = true; 374 | } 375 | param.to_tokens_subst(tokens, subst); 376 | pair.punct().to_tokens(tokens); 377 | } 378 | GenericParam::Const(param) => { 379 | if !trailing_or_empty { 380 | ::default().to_tokens(tokens); 381 | trailing_or_empty = true; 382 | } 383 | param.to_tokens(tokens); 384 | pair.punct().to_tokens(tokens); 385 | } 386 | GenericParam::Lifetime(_) => {} 387 | } 388 | } 389 | 390 | self.gt_token.unwrap_or_default().to_tokens(tokens); 391 | } 392 | } 393 | 394 | impl ToTokensSubst for TypeParam { 395 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 396 | tokens.append_all( 397 | self.attrs 398 | .iter() 399 | .filter(|attr| matches!(attr.style, AttrStyle::Outer)), 400 | ); 401 | self.ident.to_tokens(tokens); 402 | if !self.bounds.is_empty() { 403 | self.colon_token.unwrap_or_default().to_tokens(tokens); 404 | self.bounds.to_tokens_subst(tokens, subst); 405 | } 406 | if let Some(default) = &self.default { 407 | self.eq_token.unwrap_or_default().to_tokens(tokens); 408 | default.to_tokens(tokens); 409 | } 410 | } 411 | } 412 | 413 | impl ToTokensSubst for WhereClause { 414 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 415 | if !self.predicates.is_empty() { 416 | self.where_token.to_tokens(tokens); 417 | self.predicates.to_tokens_subst(tokens, subst); 418 | } 419 | } 420 | } 421 | 422 | impl ToTokensSubst for Punctuated 423 | where 424 | T: ToTokensSubst, 425 | P: ToTokens, 426 | { 427 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 428 | for pair in self.pairs() { 429 | pair.value().to_tokens_subst(tokens, subst); 430 | if let Some(punct) = pair.punct() { 431 | punct.to_tokens(tokens); 432 | } 433 | } 434 | } 435 | } 436 | 437 | impl ToTokensSubst for WherePredicate { 438 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 439 | match self { 440 | WherePredicate::Type(ty) => ty.to_tokens_subst(tokens, subst), 441 | WherePredicate::Lifetime(lt) => lt.to_tokens(tokens), 442 | } 443 | } 444 | } 445 | 446 | impl ToTokensSubst for PredicateType { 447 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 448 | self.lifetimes.to_tokens(tokens); 449 | self.bounded_ty.to_tokens(tokens); 450 | self.colon_token.to_tokens(tokens); 451 | self.bounds.to_tokens_subst(tokens, subst); 452 | } 453 | } 454 | 455 | impl ToTokensSubst for TypeParamBound { 456 | fn to_tokens_subst(&self, tokens: &mut TokenStream, subst: &TokenStream) { 457 | match self { 458 | TypeParamBound::TraitSubst(_) => tokens.append_all(quote! { #subst }), 459 | TypeParamBound::Other(bound) => bound.to_tokens(tokens), 460 | } 461 | } 462 | } 463 | } 464 | 465 | fn map_type_param_bound(bound: &syn::TypeParamBound) -> TypeParamBound { 466 | TypeParamBound::Other(bound.clone()) 467 | } 468 | 469 | fn map_generic_param(param: &syn::GenericParam) -> GenericParam { 470 | match param { 471 | syn::GenericParam::Type(ty) => GenericParam::Type(TypeParam { 472 | attrs: ty.attrs.clone(), 473 | ident: ty.ident.clone(), 474 | colon_token: ty.colon_token, 475 | bounds: Punctuated::from_iter( 476 | ty.bounds 477 | .pairs() 478 | .map(|pair| map_pair(pair, map_type_param_bound)), 479 | ), 480 | eq_token: ty.eq_token, 481 | default: ty.default.clone(), 482 | }), 483 | syn::GenericParam::Lifetime(lt) => GenericParam::Lifetime(lt.clone()), 484 | syn::GenericParam::Const(c) => GenericParam::Const(c.clone()), 485 | } 486 | } 487 | 488 | fn map_pair V>(pair: Pair, f: F) -> Pair { 489 | match pair { 490 | Pair::Punctuated(value, punct) => Pair::Punctuated(f(value), punct.clone()), 491 | Pair::End(value) => Pair::End(f(value)), 492 | } 493 | } 494 | 495 | impl Generics { 496 | /// Generate (`impl_generics`, `where_clause`) tokens 497 | /// 498 | /// Combines generics from `self` and `item_generics`. 499 | /// 500 | /// This is the equivalent of the first and third items output by 501 | /// [`syn::Generics::split_for_impl`]. Any instance of `trait` as a parameter 502 | /// bound is replaced by `subst`. 503 | /// 504 | /// Note: use `ty_generics` from [`syn::Generics::split_for_impl`] or 505 | /// [`Self::ty_generics`] as appropriate. 506 | pub fn impl_generics( 507 | mut self, 508 | item_generics: &syn::Generics, 509 | subst: &TokenStream, 510 | ) -> (TokenStream, TokenStream) { 511 | let mut impl_generics = quote! {}; 512 | if self.params.is_empty() { 513 | item_generics.to_tokens(&mut impl_generics); 514 | } else { 515 | if !self.params.empty_or_trailing() { 516 | self.params.push_punct(Default::default()); 517 | } 518 | self.params.extend( 519 | item_generics 520 | .params 521 | .pairs() 522 | .map(|pair| map_pair(pair, map_generic_param)), 523 | ); 524 | self.to_tokens_subst(&mut impl_generics, subst); 525 | } 526 | 527 | let where_clause = clause_to_toks( 528 | &self.where_clause, 529 | item_generics.where_clause.as_ref(), 530 | subst, 531 | ); 532 | (impl_generics, where_clause) 533 | } 534 | /// Generate `ty_generics` tokens 535 | /// 536 | /// Combines generics from `self` and `item_generics`. 537 | /// 538 | /// This is the equivalent to the second item output by 539 | /// [`syn::Generics::split_for_impl`]. 540 | pub fn ty_generics(&self, item_generics: &syn::Generics) -> TokenStream { 541 | let mut toks = TokenStream::new(); 542 | let tokens = &mut toks; 543 | 544 | if self.params.is_empty() { 545 | let (_, ty_generics, _) = item_generics.split_for_impl(); 546 | ty_generics.to_tokens(tokens); 547 | return toks; 548 | } 549 | 550 | self.lt_token.unwrap_or_default().to_tokens(tokens); 551 | 552 | // Print lifetimes before types and consts (see syn impl) 553 | for (def, punct) in self 554 | .params 555 | .pairs() 556 | .filter_map(|param| { 557 | if let GenericParam::Lifetime(def) = *param.value() { 558 | Some((def, param.punct().map(|p| **p).unwrap_or_default())) 559 | } else { 560 | None 561 | } 562 | }) 563 | .chain(item_generics.params.pairs().filter_map(|param| { 564 | if let syn::GenericParam::Lifetime(def) = *param.value() { 565 | Some((def, param.punct().map(|p| **p).unwrap_or_default())) 566 | } else { 567 | None 568 | } 569 | })) 570 | { 571 | // Leave off the lifetime bounds and attributes 572 | def.lifetime.to_tokens(tokens); 573 | punct.to_tokens(tokens); 574 | } 575 | 576 | for param in self.params.pairs() { 577 | match *param.value() { 578 | GenericParam::Lifetime(_) => continue, 579 | GenericParam::Type(param) => { 580 | // Leave off the type parameter defaults 581 | param.ident.to_tokens(tokens); 582 | } 583 | GenericParam::Const(param) => { 584 | // Leave off the const parameter defaults 585 | param.ident.to_tokens(tokens); 586 | } 587 | } 588 | param 589 | .punct() 590 | .map(|p| **p) 591 | .unwrap_or_default() 592 | .to_tokens(tokens); 593 | } 594 | for param in item_generics.params.pairs() { 595 | match *param.value() { 596 | syn::GenericParam::Lifetime(_) => continue, 597 | syn::GenericParam::Type(param) => { 598 | // Leave off the type parameter defaults 599 | param.ident.to_tokens(tokens); 600 | } 601 | syn::GenericParam::Const(param) => { 602 | // Leave off the const parameter defaults 603 | param.ident.to_tokens(tokens); 604 | } 605 | } 606 | param 607 | .punct() 608 | .map(|p| **p) 609 | .unwrap_or_default() 610 | .to_tokens(tokens); 611 | } 612 | 613 | self.gt_token.unwrap_or_default().to_tokens(tokens); 614 | 615 | toks 616 | } 617 | } 618 | 619 | /// Generate a `where_clause` 620 | /// 621 | /// This merges a [`WhereClause`] with a [`syn::WhereClause`], replacing any 622 | /// instance of `trait` as a parameter bound in `wc` with `subst`. 623 | pub fn clause_to_toks( 624 | wc: &Option, 625 | item_wc: Option<&syn::WhereClause>, 626 | subst: &TokenStream, 627 | ) -> TokenStream { 628 | match (wc, item_wc) { 629 | (None, None) => quote! {}, 630 | (Some(wc), None) => { 631 | let mut toks = quote! {}; 632 | wc.to_tokens_subst(&mut toks, subst); 633 | toks 634 | } 635 | (None, Some(wc)) => quote! { #wc }, 636 | (Some(wc), Some(item_wc)) => { 637 | let mut toks = quote! { #item_wc }; 638 | if !item_wc.predicates.empty_or_trailing() { 639 | toks.append_all(quote! { , }); 640 | } 641 | wc.predicates.to_tokens_subst(&mut toks, subst); 642 | toks 643 | } 644 | } 645 | } 646 | -------------------------------------------------------------------------------- /lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! # Impl-tools-lib 7 | //! 8 | //! To implement the proc-macros, copy and modify the 9 | //! [`impl-tools`](https://github.com/kas-gui/impl-tools/) crate, which is 10 | //! merely documentation plus wrappers around this crate. 11 | 12 | #![deny(missing_docs)] 13 | // Lint advocates use of bool::then_some, stablizied in rustc 1.62.0 14 | #![allow(clippy::unnecessary_lazy_evaluations)] 15 | #![allow(clippy::style)] 16 | 17 | pub mod anon; 18 | pub mod autoimpl; 19 | mod default; 20 | pub mod fields; 21 | pub mod generics; 22 | pub mod scope; 23 | 24 | pub use default::ImplDefault; 25 | use proc_macro2::Span; 26 | use syn::Ident; 27 | 28 | /// Tool to make a formatted [`Ident`](struct@Ident) 29 | pub struct IdentFormatter(String); 30 | impl IdentFormatter { 31 | /// Construct a formatter 32 | pub fn new() -> Self { 33 | IdentFormatter(String::with_capacity(32)) 34 | } 35 | 36 | /// Construct a new [`Ident`](struct@Ident) 37 | pub fn make(&mut self, args: std::fmt::Arguments, span: Span) -> Ident { 38 | use std::fmt::Write; 39 | 40 | self.0.clear(); 41 | self.0.write_fmt(args).unwrap(); 42 | Ident::new(&self.0, span) 43 | } 44 | 45 | /// Construct a new [`Ident`](struct@Ident), using [`Span::call_site`] 46 | /// 47 | /// # Example 48 | /// 49 | /// ``` 50 | /// # use impl_tools_lib::IdentFormatter; 51 | /// let mut idfmt = IdentFormatter::new(); 52 | /// let ident = idfmt.make_call_site(format_args!("x{}", 6)); 53 | /// assert_eq!(ident, "x6"); 54 | /// ``` 55 | #[inline] 56 | pub fn make_call_site(&mut self, args: std::fmt::Arguments) -> Ident { 57 | self.make(args, Span::call_site()) 58 | } 59 | } 60 | 61 | /// Simple, allocation-free path representation 62 | #[derive(PartialEq, Eq)] 63 | pub struct SimplePath(&'static [&'static str]); 64 | 65 | impl std::fmt::Display for SimplePath { 66 | fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 67 | if !self.0.is_empty() { 68 | write!(f, "{}", self.0[0])?; 69 | for component in &self.0[1..] { 70 | write!(f, "::{}", component)?; 71 | } 72 | } 73 | 74 | Ok(()) 75 | } 76 | } 77 | 78 | impl SimplePath { 79 | /// Construct, verifying validity 80 | /// 81 | /// If the first component is an empty string, this is treated as a leading 82 | /// colon (e.g. `["", "abc", "Def"] == `::abc::Def`). No other component may 83 | /// be empty. At least one non-empty component is required. 84 | /// 85 | /// Panics if requirements are not met. 86 | pub fn new(path: &'static [&'static str]) -> Self { 87 | let mut is_empty = false; 88 | for (i, s) in path.iter().enumerate() { 89 | is_empty = is_empty && s.is_empty(); 90 | if i > 0 && s.is_empty() { 91 | panic!("empty component"); 92 | } 93 | } 94 | if is_empty { 95 | panic!("empty path"); 96 | } 97 | SimplePath(path) 98 | } 99 | 100 | /// True if this matches a [`syn::Path`] 101 | /// 102 | /// This must match the path exactly, with two exceptions: 103 | /// 104 | /// - if `path` has no leading colon but `self` does (empty first 105 | /// component), the paths may still match 106 | /// - if the first component of `self` is `core` or `alloc` but the first 107 | /// component of `path` is `std`, the paths may still match 108 | pub fn matches(&self, path: &syn::Path) -> bool { 109 | let mut q = self.0; 110 | assert!(!q.is_empty()); 111 | if path.leading_colon.is_some() && !q[0].is_empty() { 112 | return false; 113 | } 114 | if q[0].is_empty() { 115 | q = &q[1..]; 116 | } 117 | 118 | if path.segments.len() != q.len() { 119 | return false; 120 | } 121 | 122 | let mut first = true; 123 | for (x, y) in path.segments.iter().zip(q.iter()) { 124 | if !x.arguments.is_empty() { 125 | return false; 126 | } 127 | 128 | #[allow(clippy::if_same_then_else)] 129 | if x.ident == y { 130 | } else if first && (*y == "core" || *y == "alloc") && x.ident == "std" { 131 | } else { 132 | return false; 133 | } 134 | 135 | first = false; 136 | } 137 | 138 | true 139 | } 140 | 141 | /// True if the last component matches a [`syn::Ident`](struct@syn::Ident) 142 | pub fn matches_ident(&self, ident: &syn::Ident) -> bool { 143 | assert!(!self.0.is_empty()); 144 | self.0.iter().last().map(|s| ident == s).unwrap_or(false) 145 | } 146 | 147 | /// If input `path` has a single component with no leading colon, then 148 | /// match via [`Self::matches_ident`]; otherwise match via 149 | /// [`Self::matches`]. 150 | pub fn matches_ident_or_path(&self, path: &syn::Path) -> bool { 151 | if path.leading_colon.is_none() && path.segments.len() == 1 { 152 | let seg = &path.segments[0]; 153 | seg.arguments.is_empty() && self.matches_ident(&seg.ident) 154 | } else { 155 | self.matches(path) 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /lib/src/scope.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | //! The `impl_scope!` macro 7 | 8 | use crate::{fields::Fields, SimplePath}; 9 | use proc_macro2::{Span, TokenStream}; 10 | use proc_macro_error2::emit_error; 11 | use quote::{ToTokens, TokenStreamExt}; 12 | use syn::punctuated::Punctuated; 13 | use syn::spanned::Spanned; 14 | use syn::token::{Brace, Comma, Semi}; 15 | use syn::{ 16 | parse_quote, Attribute, FieldsNamed, GenericParam, Generics, Ident, ItemImpl, Path, Result, 17 | Token, Type, Variant, Visibility, 18 | }; 19 | 20 | pub use super::default::{find_impl_default, AttrImplDefault}; 21 | 22 | /// Attribute rule for [`Scope`] 23 | /// 24 | /// Rules are matched via a path, e.g. `&["foo"]` matches `foo` and 25 | /// `&["", "foo", "bar"]` matches `::foo::bar`. 26 | /// 27 | /// Such rules are used to expand attributes within an `impl_scope!`. 28 | pub trait ScopeAttr { 29 | /// Attribute path 30 | /// 31 | /// Rules are matched via a path, e.g. `&["foo"]` matches `foo` and 32 | /// `&["", "foo", "bar"]` matches `::foo::bar`. 33 | /// 34 | /// Note that we cannot use standard path resolution, so we match only a 35 | /// single path, as defined. 36 | fn path(&self) -> SimplePath; 37 | 38 | /// Whether repeated application is valid 39 | /// 40 | /// If this is false (the default), then an error will be omitted on 41 | /// repeated usage of the attribute. This mostly serves to emit better error 42 | /// messages in cases where the first application modifies the input. 43 | fn support_repetition(&self) -> bool { 44 | false 45 | } 46 | 47 | /// Function type of [`ScopeAttr`] rule 48 | /// 49 | /// Input arguments: 50 | /// 51 | /// - `attr`: the invoking attribute. It is suggested to parse arguments 52 | /// using [`Attribute::parse_args`] or [`Attribute::parse_args_with`]. 53 | /// - `scope`: mutable reference to the implementation scope. Usually 54 | /// an attribute rule function will read data from the scope and append its 55 | /// output to [`Scope::generated`]. 56 | fn apply(&self, attr: Attribute, scope: &mut Scope) -> Result<()>; 57 | } 58 | 59 | /// Content of items supported by [`Scope`] that are not common to all variants 60 | #[derive(Debug)] 61 | pub enum ScopeItem { 62 | /// A [`syn::ItemEnum`], minus common parts 63 | Enum { 64 | /// `enum` 65 | token: Token![enum], 66 | /// `{ ... }` 67 | brace: Brace, 68 | /// Variants of enum 69 | variants: Punctuated, 70 | }, 71 | /// A [`syn::ItemStruct`], minus common parts 72 | /// 73 | /// Uses custom [`Fields`], supporting field initializers. 74 | Struct { 75 | /// `struct` 76 | token: Token![struct], 77 | /// Fields of struct 78 | fields: Fields, 79 | }, 80 | /// A [`syn::ItemType`], minus common parts 81 | Type { 82 | /// `type` 83 | token: Token![type], 84 | /// `=` 85 | eq_token: Token![=], 86 | /// Target type 87 | ty: Box, 88 | }, 89 | /// A [`syn::ItemUnion`], minus common parts 90 | Union { 91 | /// `union` 92 | token: Token![union], 93 | /// Fields of union 94 | fields: FieldsNamed, 95 | }, 96 | } 97 | 98 | impl ScopeItem { 99 | /// Take span of `enum`/`struct`/`type`/`union` token 100 | pub fn token_span(&self) -> Span { 101 | match self { 102 | ScopeItem::Enum { token, .. } => token.span, 103 | ScopeItem::Struct { token, .. } => token.span, 104 | ScopeItem::Type { token, .. } => token.span, 105 | ScopeItem::Union { token, .. } => token.span, 106 | } 107 | } 108 | } 109 | 110 | /// Contents of `impl_scope!` 111 | /// 112 | /// `impl_scope!` input consists of one item (an `enum`, `struct`, `type` alias 113 | /// or `union`) followed by any number of implementations, and is parsed into 114 | /// this struct. 115 | /// 116 | /// On its own, `impl_scope!` provides `impl Self` syntax, with the following 117 | /// expansion done within [`Self::expand`] (after application [`ScopeAttr`] 118 | /// rules): 119 | /// 120 | /// - `impl Self { ... }` expands to `impl #impl_generics #ty_ident #ty_generics #where_clause { ... }` 121 | /// - `impl Self where #clause2 { ... }` expands similarly, but using the combined where clause 122 | /// 123 | /// The secondary utility of `impl_scope!` is to allow attribute expansion 124 | /// within itself via [`ScopeAttr`] rules. These rules may read the type item 125 | /// (which may include field initializers in the case of a struct), read 126 | /// accompanying implementations, and even modify them. 127 | #[derive(Debug)] 128 | pub struct Scope { 129 | /// Outer attributes on the item 130 | pub attrs: Vec, 131 | /// Optional `pub`, etc. 132 | pub vis: Visibility, 133 | /// Item identifier 134 | pub ident: Ident, 135 | /// Item generics 136 | pub generics: Generics, 137 | /// The item 138 | pub item: ScopeItem, 139 | /// Trailing semicolon (type alias and unit struct only) 140 | pub semi: Option, 141 | /// Implementation items 142 | pub impls: Vec, 143 | /// Output of [`ScopeAttr`] rules 144 | /// 145 | /// This does not contain any content from input, only content generated 146 | /// from [`ScopeAttr`] rules. It is appended to output as an item (usually 147 | /// a [`syn::ImplItem`]), after [`Self::impls`] items. 148 | pub generated: Vec, 149 | } 150 | 151 | impl Scope { 152 | /// Apply attribute rules 153 | /// 154 | /// The supplied `rules` are applied in the order of definition, and their 155 | /// attributes removed from the item. 156 | pub fn apply_attrs(&mut self, find_rule: impl Fn(&Path) -> Option<&'static dyn ScopeAttr>) { 157 | let mut applied: Vec<(Span, *const dyn ScopeAttr)> = Vec::new(); 158 | 159 | let mut i = 0; 160 | while i < self.attrs.len() { 161 | if let Some(rule) = find_rule(&self.attrs[i].path()) { 162 | let attr = self.attrs.remove(i); 163 | 164 | if !rule.support_repetition() { 165 | // We compare the fat pointer (including vtable address; 166 | // the data may be zero-sized and thus not unique). 167 | // We consider two rules the same when data pointers and 168 | // vtables both compare equal. 169 | let span = attr.span(); 170 | let ptr = rule as *const dyn ScopeAttr; 171 | if let Some(first) = applied.iter().find(|(_, p)| std::ptr::eq(*p, ptr)) { 172 | emit_error!(span, "repeated use of attribute not allowed"); 173 | emit_error!(first.0, "first usage here"); 174 | continue; 175 | } 176 | applied.push((span, ptr)); 177 | } 178 | 179 | if let Err(err) = rule.apply(attr, self) { 180 | emit_error!(err.span(), "{}", err); 181 | } 182 | continue; 183 | } 184 | 185 | i += 1; 186 | } 187 | } 188 | 189 | /// Expand `impl Self` 190 | /// 191 | /// This is done automatically by [`Self::expand`]. It may be called earlier 192 | /// by a [`ScopeAttr`] if required. Calling multiple times is harmless. 193 | pub fn expand_impl_self(&mut self) { 194 | for impl_ in self.impls.iter_mut() { 195 | if impl_.self_ty == parse_quote! { Self } { 196 | let mut ident = self.ident.clone(); 197 | ident.set_span(impl_.self_ty.span()); 198 | let (_, ty_generics, _) = self.generics.split_for_impl(); 199 | impl_.self_ty = parse_quote! { #ident #ty_generics }; 200 | extend_generics(&mut impl_.generics, &self.generics); 201 | } 202 | } 203 | } 204 | 205 | /// Generate the [`TokenStream`] 206 | /// 207 | /// This is a convenience function. It is valid to, instead, (1) call 208 | /// [`Self::expand_impl_self`], then (2) use the [`ToTokens`] impl on 209 | /// `Scope`. 210 | pub fn expand(mut self) -> TokenStream { 211 | self.expand_impl_self(); 212 | self.to_token_stream() 213 | } 214 | } 215 | 216 | mod parsing { 217 | use super::*; 218 | use crate::fields::parsing::data_struct; 219 | use syn::parse::{Parse, ParseStream}; 220 | use syn::spanned::Spanned; 221 | use syn::{braced, Error, Field, Lifetime, Path, TypePath, WhereClause}; 222 | 223 | impl Parse for Scope { 224 | fn parse(input: ParseStream) -> Result { 225 | let attrs = input.call(Attribute::parse_outer)?; 226 | let vis = input.parse::()?; 227 | 228 | enum Token { 229 | Enum(Token![enum]), 230 | Struct(Token![struct]), 231 | Type(Token![type]), 232 | Union(Token![union]), 233 | } 234 | let lookahead = input.lookahead1(); 235 | let token; 236 | if lookahead.peek(Token![enum]) { 237 | token = Token::Enum(input.parse()?); 238 | } else if lookahead.peek(Token![struct]) { 239 | token = Token::Struct(input.parse()?); 240 | } else if lookahead.peek(Token![type]) { 241 | token = Token::Type(input.parse()?); 242 | } else if lookahead.peek(Token![union]) { 243 | token = Token::Union(input.parse()?); 244 | } else { 245 | return Err(lookahead.error()); 246 | } 247 | 248 | let ident = input.parse::()?; 249 | let mut generics = input.parse::()?; 250 | 251 | let item; 252 | let mut semi = None; 253 | match token { 254 | Token::Enum(token) => { 255 | let (wc, brace, variants) = data_enum(input)?; 256 | generics.where_clause = wc; 257 | item = ScopeItem::Enum { 258 | token, 259 | brace, 260 | variants, 261 | }; 262 | } 263 | Token::Struct(token) => { 264 | let (wc, fields, semi_token) = data_struct(input)?; 265 | generics.where_clause = wc; 266 | semi = semi_token; 267 | item = ScopeItem::Struct { token, fields }; 268 | } 269 | Token::Type(token) => { 270 | let eq_token = input.parse()?; 271 | let ty = input.parse()?; 272 | let semi_token = input.parse()?; 273 | semi = Some(semi_token); 274 | item = ScopeItem::Type { 275 | token, 276 | eq_token, 277 | ty, 278 | }; 279 | } 280 | Token::Union(token) => { 281 | let (wc, fields) = data_union(input)?; 282 | generics.where_clause = wc; 283 | item = ScopeItem::Union { token, fields }; 284 | } 285 | } 286 | 287 | let mut impls = Vec::new(); 288 | while !input.is_empty() { 289 | impls.push(parse_impl(&ident, input)?); 290 | } 291 | 292 | Ok(Scope { 293 | attrs, 294 | vis, 295 | ident, 296 | generics, 297 | item, 298 | semi, 299 | impls, 300 | generated: vec![], 301 | }) 302 | } 303 | } 304 | 305 | fn parse_impl(in_ident: &Ident, input: ParseStream) -> Result { 306 | let mut attrs = input.call(Attribute::parse_outer)?; 307 | let defaultness: Option = input.parse()?; 308 | let unsafety: Option = input.parse()?; 309 | let impl_token: Token![impl] = input.parse()?; 310 | 311 | let has_generics = input.peek(Token![<]) 312 | && (input.peek2(Token![>]) 313 | || input.peek2(Token![#]) 314 | || (input.peek2(Ident) || input.peek2(Lifetime)) 315 | && (input.peek3(Token![:]) 316 | || input.peek3(Token![,]) 317 | || input.peek3(Token![>]) 318 | || input.peek3(Token![=])) 319 | || input.peek2(Token![const])); 320 | let mut generics: Generics = if has_generics { 321 | input.parse()? 322 | } else { 323 | Generics::default() 324 | }; 325 | 326 | let mut first_ty: Type = input.parse()?; 327 | let self_ty: Type; 328 | let trait_; 329 | 330 | let is_impl_for = input.peek(Token![for]); 331 | if is_impl_for { 332 | let for_token: Token![for] = input.parse()?; 333 | let mut first_ty_ref = &first_ty; 334 | while let Type::Group(ty) = first_ty_ref { 335 | first_ty_ref = &ty.elem; 336 | } 337 | if let Type::Path(_) = first_ty_ref { 338 | while let Type::Group(ty) = first_ty { 339 | first_ty = *ty.elem; 340 | } 341 | if let Type::Path(TypePath { qself: None, path }) = first_ty { 342 | trait_ = Some((None, path, for_token)); 343 | } else { 344 | unreachable!(); 345 | } 346 | } else { 347 | return Err(Error::new(for_token.span, "for without target trait")); 348 | } 349 | self_ty = input.parse()?; 350 | } else { 351 | trait_ = None; 352 | self_ty = first_ty; 353 | } 354 | 355 | generics.where_clause = input.parse()?; 356 | 357 | if self_ty != parse_quote! { Self } 358 | && !matches!(self_ty, Type::Path(TypePath { 359 | qself: None, 360 | path: Path { 361 | leading_colon: None, 362 | ref segments, 363 | } 364 | }) if segments.len() == 1 && segments.first().unwrap().ident == *in_ident) 365 | { 366 | return Err(Error::new( 367 | self_ty.span(), 368 | format!( 369 | "expected `Self` or `{0}` or `{0}<...>` or `Trait for Self`, etc", 370 | in_ident 371 | ), 372 | )); 373 | } 374 | 375 | let content; 376 | let brace_token = braced!(content in input); 377 | attrs.extend(Attribute::parse_inner(&content)?); 378 | 379 | let mut items = Vec::new(); 380 | while !content.is_empty() { 381 | items.push(content.parse()?); 382 | } 383 | 384 | Ok(ItemImpl { 385 | attrs, 386 | defaultness, 387 | unsafety, 388 | impl_token, 389 | generics, 390 | trait_, 391 | self_ty: Box::new(self_ty), 392 | brace_token, 393 | items, 394 | }) 395 | } 396 | 397 | pub fn data_enum( 398 | input: ParseStream, 399 | ) -> Result<(Option, Brace, Punctuated)> { 400 | let where_clause = input.parse()?; 401 | 402 | let content; 403 | let brace = braced!(content in input); 404 | let variants = content.parse_terminated(Variant::parse, Token![,])?; 405 | 406 | Ok((where_clause, brace, variants)) 407 | } 408 | 409 | pub fn data_union(input: ParseStream) -> Result<(Option, FieldsNamed)> { 410 | let where_clause = input.parse()?; 411 | let fields = parse_braced(input)?; 412 | Ok((where_clause, fields)) 413 | } 414 | 415 | pub(crate) fn parse_braced(input: ParseStream) -> Result { 416 | let content; 417 | let brace_token = braced!(content in input); 418 | let named = content.parse_terminated(Field::parse_named, Token![,])?; 419 | Ok(FieldsNamed { brace_token, named }) 420 | } 421 | } 422 | 423 | mod printing { 424 | use super::*; 425 | 426 | impl ToTokens for Scope { 427 | fn to_tokens(&self, tokens: &mut TokenStream) { 428 | tokens.append_all(self.attrs.iter()); 429 | self.vis.to_tokens(tokens); 430 | match &self.item { 431 | ScopeItem::Enum { token, .. } => token.to_tokens(tokens), 432 | ScopeItem::Struct { token, .. } => token.to_tokens(tokens), 433 | ScopeItem::Type { token, .. } => token.to_tokens(tokens), 434 | ScopeItem::Union { token, .. } => token.to_tokens(tokens), 435 | } 436 | self.ident.to_tokens(tokens); 437 | self.generics.to_tokens(tokens); 438 | match &self.item { 439 | ScopeItem::Enum { 440 | brace, variants, .. 441 | } => { 442 | self.generics.where_clause.to_tokens(tokens); 443 | brace.surround(tokens, |tokens| { 444 | variants.to_tokens(tokens); 445 | }); 446 | } 447 | ScopeItem::Struct { fields, .. } => match fields { 448 | Fields::Named(fields) => { 449 | self.generics.where_clause.to_tokens(tokens); 450 | fields.to_tokens(tokens); 451 | } 452 | Fields::Unnamed(fields) => { 453 | fields.to_tokens(tokens); 454 | self.generics.where_clause.to_tokens(tokens); 455 | } 456 | Fields::Unit => { 457 | self.generics.where_clause.to_tokens(tokens); 458 | } 459 | }, 460 | ScopeItem::Type { eq_token, ty, .. } => { 461 | self.generics.where_clause.to_tokens(tokens); 462 | eq_token.to_tokens(tokens); 463 | ty.to_tokens(tokens); 464 | } 465 | ScopeItem::Union { fields, .. } => { 466 | self.generics.where_clause.to_tokens(tokens); 467 | fields.to_tokens(tokens); 468 | } 469 | } 470 | if let Some(semi) = self.semi.as_ref() { 471 | semi.to_tokens(tokens); 472 | } 473 | 474 | tokens.append_all(self.impls.iter()); 475 | tokens.append_all(self.generated.iter()); 476 | } 477 | } 478 | } 479 | 480 | // Support impls on Self by replacing name and summing generics 481 | fn extend_generics(generics: &mut Generics, in_generics: &Generics) { 482 | if generics.lt_token.is_none() { 483 | debug_assert!(generics.params.is_empty()); 484 | debug_assert!(generics.gt_token.is_none()); 485 | generics.lt_token = in_generics.lt_token; 486 | generics.params = in_generics.params.clone(); 487 | generics.gt_token = in_generics.gt_token; 488 | } else if in_generics.lt_token.is_none() { 489 | debug_assert!(in_generics.params.is_empty()); 490 | debug_assert!(in_generics.gt_token.is_none()); 491 | } else { 492 | if !generics.params.empty_or_trailing() { 493 | generics.params.push_punct(Default::default()); 494 | } 495 | generics 496 | .params 497 | .extend(in_generics.params.clone().into_pairs()); 498 | } 499 | 500 | // Strip defaults which are legal on the struct but not on impls 501 | for param in &mut generics.params { 502 | match param { 503 | GenericParam::Type(p) => { 504 | p.eq_token = None; 505 | p.default = None; 506 | } 507 | GenericParam::Lifetime(_) => (), 508 | GenericParam::Const(p) => { 509 | p.eq_token = None; 510 | p.default = None; 511 | } 512 | } 513 | } 514 | 515 | if let Some(ref mut clause1) = generics.where_clause { 516 | if let Some(ref clause2) = in_generics.where_clause { 517 | if !clause1.predicates.empty_or_trailing() { 518 | clause1.predicates.push_punct(Default::default()); 519 | } 520 | clause1 521 | .predicates 522 | .extend(clause2.predicates.clone().into_pairs()); 523 | } 524 | } else { 525 | generics.where_clause = in_generics.where_clause.clone(); 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License in the LICENSE-APACHE file or at: 4 | // https://www.apache.org/licenses/LICENSE-2.0 5 | 6 | #![allow(clippy::needless_doctest_main)] 7 | // Lint advocates use of bool::then_some, stablizied in rustc 1.62.0 8 | #![allow(clippy::unnecessary_lazy_evaluations)] 9 | 10 | //! # Impl-tools 11 | //! 12 | //! [`#[autoimpl]`](macro@autoimpl) is an alternative to 13 | //! [`#[derive]`](macro@derive) with more features (also usable on traits). 14 | //! 15 | //! [`#[impl_default]`](macro@impl_default) is shorthand for implementing 16 | //! [`Default`] with an explicit default value. 17 | //! It supports structs and enums. 18 | //! 19 | //! [`impl_scope!`] is a function-like macro used to define a type together with 20 | //! its implementations. This allows: 21 | //! 22 | //! - `impl Self` syntax (avoid repeated definitions of generics) 23 | //! - Evaluation of some more complex attribute macros 24 | //! 25 | //! [`impl_anon!`] is a function-like macro used to define and instantiate a 26 | //! unique (single-use) type. It supports everything supported by [`impl_scope!`] 27 | //! plus field initializers and (limited) automatic typing of fields. 28 | //! 29 | //! User-extensions to both [`#[autoimpl]`](macro@autoimpl) and [`impl_scope!`] 30 | //! are possible with a custom proc-macro crate depending on 31 | //! [impl-tools-lib](https://crates.io/crates/impl-tools-lib). 32 | 33 | #[cfg(doctest)] 34 | doc_comment::doctest!("../README.md"); 35 | 36 | extern crate proc_macro; 37 | 38 | use lib::{anon, scope}; 39 | use proc_macro::TokenStream; 40 | use proc_macro_error2::{emit_call_site_error, proc_macro_error}; 41 | use syn::parse_macro_input; 42 | 43 | use impl_tools_lib::{self as lib, autoimpl}; 44 | 45 | /// Impl [`Default`] with given field or type initializers 46 | /// 47 | /// This macro may be used in one of two ways. 48 | /// 49 | /// ### Type-level initializer 50 | /// 51 | /// ``` 52 | /// # use impl_tools::impl_default; 53 | /// /// A simple enum; default value is Blue 54 | /// #[impl_default(Colour::Blue)] 55 | /// enum Colour { 56 | /// Red, 57 | /// Green, 58 | /// Blue, 59 | /// } 60 | /// 61 | /// fn main() { 62 | /// assert!(matches!(Colour::default(), Colour::Blue)); 63 | /// } 64 | /// ``` 65 | /// 66 | /// A where clause is optional: `#[impl_default(EXPR where BOUNDS)]`. 67 | /// 68 | /// ### Field-level initializer 69 | /// 70 | /// This variant only supports structs. Fields specified as `name: type = expr` 71 | /// will be initialized with `expr`, while other fields will be initialized with 72 | /// `Default::default()`. 73 | /// 74 | /// ``` 75 | /// # use impl_tools::{impl_default, impl_scope}; 76 | /// impl_scope! { 77 | /// #[impl_default] 78 | /// struct Person { 79 | /// name: String = "Jane Doe".to_string(), 80 | /// age: u32 = 72, 81 | /// occupation: String, 82 | /// } 83 | /// } 84 | /// 85 | /// fn main() { 86 | /// let person = Person::default(); 87 | /// assert_eq!(person.name, "Jane Doe"); 88 | /// assert_eq!(person.age, 72); 89 | /// assert_eq!(person.occupation, ""); 90 | /// } 91 | /// ``` 92 | /// 93 | /// A where clause is optional: `#[impl_default(where BOUNDS)]`. 94 | #[proc_macro_attribute] 95 | #[proc_macro_error] 96 | pub fn impl_default(args: TokenStream, item: TokenStream) -> TokenStream { 97 | let mut toks = item.clone(); 98 | match syn::parse::(args) { 99 | Ok(attr) => toks.extend(TokenStream::from(attr.expand(item.into()))), 100 | Err(err) => { 101 | emit_call_site_error!(err); 102 | // Since this form of invocation only adds implementations, we can 103 | // safely output the original item, thus reducing secondary errors. 104 | } 105 | } 106 | toks 107 | } 108 | 109 | /// An alternative to the standard [`macro@derive`] macro 110 | /// 111 | /// This macro may be used: 112 | /// 113 | /// - [On a type definition](#on-type-definitions), to implement a specified trait 114 | /// - [On a trait definition](#on-trait-definitions), to implement the trait for specified types 115 | /// supporting [`Deref`] 116 | /// 117 | /// # On type definitions 118 | /// 119 | /// `#[autoimpl]` on type definitions functions similarly to [`#[derive]`](macro@derive). The differences are as follows. 120 | /// 121 | /// There is no implied bound on generic parameters. Instead, bounds must be specified explicitly, using syntax like `where T: Clone`. The special syntax `where T: trait` may be used where `trait` desugars to the target trait for each implementation. An example: 122 | /// ``` 123 | /// # use impl_tools::autoimpl; 124 | /// #[autoimpl(Clone, Debug where T: trait)] 125 | /// struct Wrapper(pub T); 126 | /// ``` 127 | /// 128 | /// ### `ignore` 129 | /// 130 | /// Traits like [`Debug`] may be implemented while `ignore`-ing some fields, for example: 131 | /// ``` 132 | /// # use impl_tools::autoimpl; 133 | /// #[autoimpl(Debug ignore self.f)] 134 | /// struct PairWithFn { 135 | /// x: f32, 136 | /// y: f32, 137 | /// f: fn(&T), 138 | /// } 139 | /// ``` 140 | /// 141 | /// ### `using` 142 | /// 143 | /// Traits like [`Deref`] may be implemented by `using` a named field, for example: 144 | /// ``` 145 | /// # use impl_tools::autoimpl; 146 | /// #[autoimpl(Deref, DerefMut using self.1)] 147 | /// struct AnnotatedWrapper(String, T); 148 | /// ``` 149 | /// In the above example, [`Deref::Target`] will be implemented as `T` (the type 150 | /// of the field `self.1`). The `Target` type may instead be specified explicitly: 151 | /// ``` 152 | /// # use impl_tools::autoimpl; 153 | /// #[autoimpl(Deref using self.0)] 154 | /// struct MyBoxingWrapper(Box); 155 | /// ``` 156 | /// 157 | /// ## Supported traits 158 | /// 159 | /// | Path | *ignore* | *using* | *notes* | 160 | /// |----- |--- |--- |--- | 161 | /// | [`::core::borrow::Borrow`] | - | borrow target | `T` is type of target field | 162 | /// | [`::core::borrow::BorrowMut`] | - | borrow target | `T` is type of target field | 163 | /// | [`::core::clone::Clone`] | yes | - | ignored fields use `Default::default()` | 164 | /// | [`::core::cmp::Eq`] | * | - | *allowed with `PartialEq` | 165 | /// | [`::core::cmp::Ord`] | yes | - | | 166 | /// | [`::core::cmp::PartialEq`] | yes | - | | 167 | /// | [`::core::cmp::PartialOrd`] | yes | - | | 168 | /// | [`::core::convert::AsRef`] | - | ref target | `T` is type of target field | 169 | /// | [`::core::convert::AsMut`] | - | ref target | `T` is type of target field | 170 | /// | [`::core::default::Default`] | - | - | [`macro@impl_default`] is a more flexible alternative | 171 | /// | [`::core::fmt::Debug`] | yes | - | | 172 | /// | [`::core::hash::Hash`] | yes | - | | 173 | /// | [`::core::marker::Copy`] | * | - | *allowed with `Clone` | 174 | /// | [`::core::ops::Deref`] | - | deref target | See [`Deref::Target` type](#dereftarget-type) below | 175 | /// | [`::core::ops::DerefMut`] | - | deref target | | 176 | /// 177 | /// Traits are matched using the path, as follows: 178 | /// 179 | /// - Only the last component, e.g. `#[autoimpl(Clone)]` 180 | /// - The full path with leading `::`, e.g. `#[autoimpl(::core::clone::Clone)]` 181 | /// - The full path without leading `::`, e.g. `#[autoimpl(core::clone::Clone)]` 182 | /// - The full path with/without leading `::`, using `std` instead of `core` or `alloc`, 183 | /// e.g. `#[autoimpl(std::clone::Clone)]` 184 | /// 185 | /// ## Parameter syntax 186 | /// 187 | /// > _ParamsMulti_ :\ 188 | /// >    ( _Trait_ ),+ _Using_? _Ignores_? _WhereClause_? 189 | /// > 190 | /// > _Using_ :\ 191 | /// >    `using` `self` `.` _Member_ 192 | /// > 193 | /// > _Ignores_ :\ 194 | /// >    `ignore` ( `self` `.` _Member_ ),+ 195 | /// > 196 | /// > _WhereClause_ :\ 197 | /// >    `where` ( _WherePredicate_ ),* 198 | /// 199 | /// **Targets:** each *Trait* listed is implemented for the annotated type. 200 | /// 201 | /// 202 | /// # On trait definitions 203 | /// 204 | /// `#[autoimpl]` on trait definitions generates an implementation of that trait 205 | /// for the given targets. This functions using an implementation of [`Deref`] 206 | /// (and, where required, [`DerefMut`]) to lower the target type to some other 207 | /// type supporting the trait. We call this latter type the **definitive type**. 208 | /// 209 | /// It is required that the target type(s) implemented are generic over some 210 | /// type parameter(s). These generic parameters are introduced using `for<..>`. 211 | /// It is further required that at least one generic parameter has a bound on 212 | /// `trait`; the first such parameter is inferred to be the *definitive type*. 213 | /// 214 | /// For example, the following usage implements `MyTrait` for targets `&T`, 215 | /// `&mut T` and `Box` using definitive type `T`: 216 | /// ``` 217 | /// # use impl_tools::autoimpl; 218 | /// #[autoimpl(for &T, &mut T, Box)] 219 | /// trait MyTrait { 220 | /// fn f(&self) -> String; 221 | /// } 222 | /// ``` 223 | /// The expansion for target `Box` looks like: 224 | /// ``` 225 | /// # trait MyTrait { 226 | /// # fn f(&self) -> String; 227 | /// # } 228 | /// #[automatically_derived] 229 | /// impl MyTrait for Box { 230 | /// fn f(&self) -> String { 231 | /// ::f(self) 232 | /// } 233 | /// } 234 | /// ``` 235 | /// 236 | /// ## Generics 237 | /// 238 | /// Traits using generics and trait items using generics are, for the most part, 239 | /// supported. 240 | /// 241 | /// Items with a where clause with a type bound on `Self` are not supported 242 | /// since the item is not guaranteed to exist on the definitive type. 243 | /// Exception: methods with a default implementation (in this case the item is 244 | /// skipped). 245 | /// 246 | /// An example: 247 | /// ``` 248 | /// # use impl_tools::autoimpl; 249 | /// # use std::fmt::Debug; 250 | /// #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] 251 | /// trait G 252 | /// where 253 | /// V: Debug, 254 | /// { 255 | /// fn g(&self) -> V; 256 | /// 257 | /// fn s(&self, f: impl Fn(V) -> X) -> X 258 | /// where 259 | /// Self: Sized, 260 | /// { 261 | /// f(self.g()) 262 | /// } 263 | /// } 264 | /// ``` 265 | /// 266 | /// ## Parameter syntax 267 | /// 268 | /// > _ParamsTrait_ :\ 269 | /// >    `for` _Generics_ ( _Type_ ),+ _WhereClause_? 270 | /// 271 | /// [`Deref`]: std::ops::Deref 272 | /// [`Deref::Target`]: std::ops::Deref::Target 273 | /// [`DerefMut`]: std::ops::DerefMut 274 | #[proc_macro_attribute] 275 | #[proc_macro_error] 276 | pub fn autoimpl(attr: TokenStream, item: TokenStream) -> TokenStream { 277 | let mut toks = item.clone(); 278 | match syn::parse::(attr) { 279 | Ok(autoimpl::Attr::ForDeref(ai)) => toks.extend(TokenStream::from(ai.expand(item.into()))), 280 | Ok(autoimpl::Attr::ImplTraits(ai)) => { 281 | // We could use lazy_static to construct a HashMap for fast lookups, 282 | // but given the small number of impls a "linear map" is fine. 283 | let find_impl = |path: &syn::Path| { 284 | autoimpl::STD_IMPLS 285 | .iter() 286 | .cloned() 287 | .find(|impl_| impl_.path().matches_ident_or_path(path)) 288 | }; 289 | toks.extend(TokenStream::from(ai.expand(item.into(), find_impl))) 290 | } 291 | Err(err) => { 292 | emit_call_site_error!(err); 293 | // Since autoimpl only adds implementations, we can safely output 294 | // the original item, thus reducing secondary errors. 295 | } 296 | } 297 | toks 298 | } 299 | 300 | /// Implement a type with `impl Self` syntax 301 | /// 302 | /// This macro facilitates definition of a type (struct, enum or union) plus 303 | /// implementations via `impl Self { .. }` syntax: `Self` is expanded to the 304 | /// type's name, including generics and bounds (as defined on the type). 305 | /// 306 | /// Caveat: `rustfmt` can not yet format contents (see 307 | /// [rustfmt#5254](https://github.com/rust-lang/rustfmt/issues/5254), 308 | /// [rustfmt#5538](https://github.com/rust-lang/rustfmt/pull/5538)). 309 | /// 310 | /// ## Special attribute macros 311 | /// 312 | /// Additionally, `impl_scope!` supports special attribute macros evaluated 313 | /// within its scope: 314 | /// 315 | /// - [`#[impl_default]`](macro@impl_default): implement [`Default`] using 316 | /// field initializers (which are not legal syntax outside of `impl_scope!`) 317 | /// 318 | /// Note: matching these macros within `impl_scope!` does not use path 319 | /// resolution. Using `#[impl_tools::impl_default]` would resolve the variant 320 | /// of this macro which *doesn't support* field initializers. 321 | /// 322 | /// ## Syntax 323 | /// 324 | /// > _ImplScope_ :\ 325 | /// >    `impl_scope!` `{` _ScopeItem_ _ItemImpl_ * `}` 326 | /// > 327 | /// > _ScopeItem_ :\ 328 | /// >    _ItemEnum_ | _ItemStruct_ | _ItemType_ | _ItemUnion_ 329 | /// 330 | /// That is, one type definition followed by a set of implementations. 331 | /// Impls must take one of two forms: 332 | /// 333 | /// - `impl Self { ... }` — generic parameters and bounds of the type are used 334 | /// - `impl MyType { ... }` where `MyType` matches the name of the defined type 335 | /// 336 | /// Generic parameters from the type are included implicitly with the first form. 337 | /// Additional generic parameters and where clauses are supported (parameters 338 | /// and bounds are merged). 339 | /// 340 | /// ## Example 341 | /// 342 | /// ``` 343 | /// impl_tools::impl_scope! { 344 | /// struct Pair(T, T); 345 | /// 346 | /// impl Self { 347 | /// pub fn new(a: T, b: T) -> Self { 348 | /// Pair(a, b) 349 | /// } 350 | /// } 351 | /// 352 | /// impl Self where T: Clone { 353 | /// pub fn splat(a: T) -> Self { 354 | /// let b = a.clone(); 355 | /// Pair(a, b) 356 | /// } 357 | /// } 358 | /// } 359 | /// ``` 360 | #[proc_macro_error] 361 | #[proc_macro] 362 | pub fn impl_scope(input: TokenStream) -> TokenStream { 363 | let mut scope = parse_macro_input!(input as scope::Scope); 364 | scope.apply_attrs(scope::find_impl_default); 365 | scope.expand().into() 366 | } 367 | 368 | /// Construct an anonymous struct 369 | /// 370 | /// Rust doesn't currently support [`impl Trait { ... }` expressions](https://github.com/canndrew/rfcs/blob/impl-trait-expressions/text/0000-impl-trait-expressions.md) 371 | /// or implicit typing of struct fields. This macro is a **hack** allowing that. 372 | /// 373 | /// Example: 374 | /// ``` 375 | /// use std::fmt; 376 | /// fn main() { 377 | /// let world = "world"; 378 | /// let says_hello_world = impl_tools::impl_anon! { 379 | /// struct(&'static str = world); 380 | /// impl fmt::Display for Self { 381 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 382 | /// write!(f, "hello {}", self.0) 383 | /// } 384 | /// } 385 | /// }; 386 | /// assert_eq!(format!("{}", says_hello_world), "hello world"); 387 | /// } 388 | /// ``` 389 | /// 390 | /// That is, this macro creates an anonymous struct type (must be a struct), 391 | /// which may have trait implementations, then creates an instance of that 392 | /// struct. 393 | /// 394 | /// Struct fields may have a fixed type or may be generic. Syntax is as follows: 395 | /// 396 | /// - **regular struct:** `ident: ty = value` 397 | /// - **regular struct:** `ident: ty` (uses `Default` to construct value) 398 | /// - **regular struct:** `ident = value` (type is generic without bounds) 399 | /// - **tuple struct:** `ty = value` 400 | /// - **tuple struct:** `ty` (uses `Default` to construct value) 401 | /// 402 | /// The field name, `ident`, may be `_` (anonymous field). 403 | /// 404 | /// The field type, `ty`, may be or may contain inferred types (`_`) and/or 405 | /// `impl Trait` type expressions. These are substituted with generics on the 406 | /// type. 407 | /// 408 | /// Refer to [examples](https://github.com/search?q=impl_anon+repo%3Akas-gui%2Fkas+path%3Aexamples&type=Code) for usage. 409 | #[proc_macro_error] 410 | #[proc_macro] 411 | pub fn impl_anon(input: TokenStream) -> TokenStream { 412 | let mut scope = parse_macro_input!(input as anon::Anon).into_scope(); 413 | scope.apply_attrs(scope::find_impl_default); 414 | scope.expand().into() 415 | } 416 | -------------------------------------------------------------------------------- /tests/autoimpl.rs: -------------------------------------------------------------------------------- 1 | //! Test #[autoimpl] for trait implementations 2 | 3 | // Test no_std 4 | #![no_std] 5 | extern crate alloc; 6 | use alloc::format; 7 | 8 | use core::fmt::Debug; 9 | use core::marker::PhantomData; 10 | use core::ops::DerefMut; 11 | use impl_tools::autoimpl; 12 | 13 | fn test_has_clone(_: impl Clone) {} 14 | fn test_has_copy(_: impl Copy) {} 15 | fn test_has_partial_eq(_foo: impl PartialEq) {} 16 | fn test_has_eq(_foo: impl Eq) {} 17 | 18 | #[autoimpl(std::clone::Clone, core::fmt::Debug)] 19 | struct Unit; 20 | 21 | #[test] 22 | fn unit() { 23 | test_has_clone(Unit); 24 | assert_eq!(format!("{:?}", Unit), "Unit"); 25 | } 26 | 27 | #[autoimpl(Copy, Clone, Debug ignore self.1 where T: trait)] 28 | struct Wrapper(pub T, ()); 29 | 30 | #[test] 31 | fn wrapper() { 32 | test_has_clone(Wrapper(0i32, ())); 33 | test_has_copy(Wrapper("foo", ())); 34 | assert_eq!(format!("{:?}", Wrapper((), ())), "Wrapper((), _)"); 35 | } 36 | 37 | #[autoimpl(Clone)] 38 | struct ContainsRef<'a, T: Clone> { 39 | a: &'a T, 40 | } 41 | 42 | #[test] 43 | fn contains_ref() { 44 | let a = 1; 45 | let b = ContainsRef { a: &a }; 46 | let c = b.clone(); 47 | let _ = c.a; 48 | } 49 | 50 | #[autoimpl(Clone, Default where A: trait, B: trait)] 51 | #[autoimpl(Debug ignore self.c where A: Debug)] 52 | struct X { 53 | a: A, 54 | b: B, 55 | c: PhantomData, 56 | } 57 | 58 | #[autoimpl(Clone where A: trait, B: trait)] 59 | #[autoimpl(Debug where A: Debug)] 60 | enum MaybeX { 61 | None, 62 | Some(X), 63 | } 64 | 65 | #[test] 66 | fn x() { 67 | let x = X { 68 | a: 1i8, 69 | b: "abc", 70 | c: PhantomData::, 71 | }; 72 | let y = x.clone(); 73 | assert_eq!(x.a, y.a); 74 | assert_eq!(x.b, y.b); 75 | assert_eq!(format!("{x:?}"), "X { a: 1, b: \"abc\", .. }"); 76 | 77 | let none = MaybeX::<(), (), ()>::None; 78 | assert_eq!(format!("{none:?}"), "MaybeX::None"); 79 | test_has_clone(MaybeX::Some(x)); 80 | } 81 | 82 | #[autoimpl(Deref, DerefMut using self.t)] 83 | #[autoimpl(Borrow, BorrowMut using self.t)] 84 | #[autoimpl(AsRef, AsMut using self.t)] 85 | struct Y { 86 | _s: S, 87 | t: T, 88 | } 89 | 90 | #[test] 91 | fn y() { 92 | use core::borrow::{Borrow, BorrowMut}; 93 | use core::ops::Deref; 94 | 95 | let mut y = Y { _s: (), t: 1i32 }; 96 | 97 | *y.deref_mut() = 2; 98 | assert_eq!(y.deref(), &2); 99 | assert_eq!(y.t, 2); 100 | 101 | *y.borrow_mut() = 15; 102 | assert_eq!(Borrow::::borrow(&y), &15); 103 | 104 | *y.as_mut() = 12; 105 | assert_eq!(y.as_ref(), &12); 106 | } 107 | 108 | #[autoimpl(PartialEq, PartialOrd)] 109 | #[derive(Clone, Copy, Debug)] 110 | struct Pair(f32, f32); 111 | 112 | #[test] 113 | fn pair() { 114 | use core::cmp::Ordering; 115 | let a = Pair(123.0, 0.0); 116 | 117 | assert_eq!(a, Pair(123.0, 0.0)); 118 | assert!(a != Pair(123.0, 0.1)); 119 | assert!(a != Pair(122.0, 0.0)); 120 | assert!(a != Pair(123.0, f32::NAN)); 121 | 122 | assert!(a < Pair(123.0, 0.1)); 123 | assert!(a > Pair(123.0, -0.1)); 124 | assert!(a > Pair(122.0, 0.1)); 125 | assert_eq!(a.partial_cmp(&Pair(123.0, 0.0)), Some(Ordering::Equal)); 126 | assert_eq!(a.partial_cmp(&Pair(123.0, f32::NAN)), None); 127 | } 128 | 129 | #[autoimpl(Clone, Debug)] 130 | #[autoimpl(PartialEq, Eq, PartialOrd, Ord, Hash ignore self._f)] 131 | struct MixedComponents { 132 | i: i32, 133 | s: &'static str, 134 | _f: fn() -> i32, 135 | } 136 | 137 | #[test] 138 | fn mixed_components() { 139 | use core::cmp::Ordering; 140 | 141 | let a = MixedComponents { 142 | i: 31, 143 | s: "abc", 144 | _f: || 9, 145 | }; 146 | assert_eq!(a, a); 147 | assert_eq!(a.cmp(&a), Ordering::Equal); 148 | assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal)); 149 | let a_hash = xx_hash_64_0(&a); 150 | assert_eq!(a_hash, 877288650698020945); 151 | 152 | let b = MixedComponents { 153 | i: 31, 154 | s: "abc", 155 | _f: || 14, 156 | }; 157 | assert_eq!(a, b); // field f differs but is ignored 158 | assert_eq!(a.cmp(&b), Ordering::Equal); 159 | assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal)); 160 | assert_eq!(xx_hash_64_0(&b), a_hash); 161 | 162 | let mut c = a.clone(); 163 | c.i = 2; 164 | assert!(a != c); 165 | assert!(a > c); 166 | assert_eq!(Some(a.cmp(&c)), a.partial_cmp(&c)); 167 | assert!(xx_hash_64_0(&c) != a_hash); 168 | 169 | let mut d = a.clone(); 170 | d.s = "def"; 171 | assert!(a != d); 172 | assert!(a < d); 173 | assert_eq!(Some(a.cmp(&d)), a.partial_cmp(&d)); 174 | assert!(xx_hash_64_0(&d) != a_hash); 175 | } 176 | 177 | fn xx_hash_64_0(x: impl core::hash::Hash) -> u64 { 178 | let mut hasher = twox_hash::XxHash64::with_seed(0); 179 | x.hash(&mut hasher); 180 | core::hash::Hasher::finish(&hasher) 181 | } 182 | 183 | #[allow(unused)] 184 | #[autoimpl(PartialEq, Eq ignore self.bar where S: trait)] 185 | struct Foo { 186 | foo: S, 187 | bar: u8, 188 | ptr: *const T, 189 | } 190 | #[test] 191 | fn foo() { 192 | let x = || 1; 193 | let ptr = &x as *const _; 194 | test_has_partial_eq(Foo { 195 | foo: 1f32, 196 | bar: 0, 197 | ptr, 198 | }); 199 | // Expected to fail: 200 | // test_has_eq(Foo { foo: 1f32, bar: 0, ptr }); 201 | test_has_eq(Foo { 202 | foo: 1i32, 203 | bar: 0, 204 | ptr, 205 | }); 206 | } 207 | -------------------------------------------------------------------------------- /tests/autoimpl_enum.rs: -------------------------------------------------------------------------------- 1 | //! Test #[autoimpl] for trait implementations 2 | 3 | // Test no_std 4 | #![no_std] 5 | extern crate alloc; 6 | use alloc::format; 7 | 8 | use impl_tools::{autoimpl, impl_default}; 9 | 10 | fn test_has_clone(_: impl Clone) {} 11 | fn test_has_copy(_: impl Copy) {} 12 | 13 | #[allow(unused)] 14 | #[autoimpl(std::clone::Clone, core::fmt::Debug)] 15 | #[autoimpl(std::cmp::PartialEq, std::cmp::Eq, core::hash::Hash)] 16 | enum Void {} 17 | 18 | #[autoimpl(std::marker::Copy, std::clone::Clone, core::fmt::Debug)] 19 | #[autoimpl(PartialEq, Eq, Hash)] 20 | enum Variants { 21 | A, 22 | B(()), 23 | C(&'static str, i32), 24 | #[allow(unused)] 25 | D { 26 | x: i32, 27 | y: i32, 28 | }, 29 | } 30 | 31 | #[test] 32 | fn variants() { 33 | test_has_copy(Variants::A); 34 | test_has_clone(Variants::A); 35 | assert_eq!(format!("{:?}", Variants::A), "Variants::A"); 36 | assert_eq!(format!("{:?}", Variants::B(())), "Variants::B(())"); 37 | assert_eq!( 38 | format!("{:?}", Variants::C("abc", 3)), 39 | "Variants::C(\"abc\", 3)" 40 | ); 41 | assert_eq!( 42 | format!("{:?}", Variants::D { x: 3, y: 2 }), 43 | "Variants::D { x: 3, y: 2 }" 44 | ); 45 | } 46 | 47 | #[autoimpl(Copy, Clone, Debug where T: trait)] 48 | #[impl_default(MyOption::None)] 49 | #[autoimpl(PartialEq, Eq where T: trait)] 50 | enum MyOption { 51 | None, 52 | Some(T), 53 | } 54 | 55 | #[test] 56 | fn my_option() { 57 | test_has_clone(MyOption::::default()); 58 | test_has_copy(MyOption::Some(1)); 59 | assert_eq!(format!("{:?}", MyOption::Some(1)), "MyOption::Some(1)"); 60 | } 61 | -------------------------------------------------------------------------------- /tests/for_deref.rs: -------------------------------------------------------------------------------- 1 | //! Test #[autoimpl] for trait references 2 | 3 | // Test no_std 4 | #![no_std] 5 | extern crate alloc; 6 | use alloc::boxed::Box; 7 | 8 | use core::fmt::Debug; 9 | use core::ops::Deref; 10 | use impl_tools::autoimpl; 11 | 12 | #[autoimpl(for<'a, T: trait> &'a mut T, Box)] 13 | trait Z { 14 | const A: i32; 15 | 16 | fn f(&self); 17 | fn g(&mut self, a: i32, b: &Self::B); 18 | 19 | type B; 20 | } 21 | 22 | impl Z for () { 23 | const A: i32 = 10; 24 | 25 | fn f(&self) {} 26 | fn g(&mut self, _: i32, _: &i32) {} 27 | 28 | type B = i32; 29 | } 30 | 31 | #[test] 32 | fn z() { 33 | fn impls_z(mut z: impl Z) { 34 | z.f(); 35 | z.g(1, &2); 36 | } 37 | 38 | impls_z(()); 39 | impls_z(&mut ()); 40 | impls_z(Box::new(())); 41 | } 42 | 43 | macro_rules! ident { 44 | ($t:tt) => { 45 | $t 46 | }; 47 | } 48 | 49 | #[autoimpl(for<'a, T> &'a mut T where T: trait + ?Sized)] 50 | pub trait BitRead2 { 51 | fn copy_to(&mut self, mut _n: u64) -> Result<(), core::convert::Infallible> { 52 | Ok(()) 53 | } 54 | 55 | fn p(&self, ident!(_x): i32); 56 | 57 | #[allow(unused_parens)] 58 | fn fn_with_parens(&self, (_x): i32) {} 59 | 60 | fn fn_with_path(&self, core::any::TypeId { .. }: core::any::TypeId) {} 61 | } 62 | 63 | #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] 64 | trait G 65 | where 66 | V: Debug, 67 | { 68 | fn g(&self) -> V; 69 | 70 | fn s(&self, f: impl Fn(V) -> X) -> X 71 | where 72 | Self: Sized, 73 | { 74 | f(self.g()) 75 | } 76 | } 77 | 78 | #[test] 79 | fn g() { 80 | struct S; 81 | impl G for S { 82 | fn g(&self) -> i32 { 83 | 123 84 | } 85 | } 86 | 87 | fn impls_g(g: impl G) { 88 | assert_eq!(g.g(), 123); 89 | assert!(g.s(|x| x == 123)); 90 | } 91 | 92 | impls_g(S); 93 | impls_g(&S); 94 | impls_g(&&S); 95 | impls_g(&mut S); 96 | impls_g(&&mut S); 97 | impls_g(&S as &dyn G); 98 | impls_g(Box::new(S)); 99 | impls_g(&mut &Box::new(S)); 100 | impls_g(Box::new(S) as Box>); 101 | impls_g(&mut (Box::new(S) as Box>)); 102 | } 103 | 104 | #[allow(unused)] 105 | #[autoimpl(for &T)] 106 | trait H> 107 | where 108 | X: Debug, 109 | { 110 | } 111 | 112 | #[cfg(rustc_1_65)] 113 | #[autoimpl(for Box)] 114 | trait Gat { 115 | type T; 116 | 117 | type R<'a, X>: core::ops::Deref> 118 | where 119 | X: 'a; 120 | } 121 | 122 | #[cfg(rustc_1_65)] 123 | #[test] 124 | fn gat() { 125 | struct S; 126 | impl Gat for S { 127 | type T = X; 128 | 129 | // Our MSRV doesn't support the preferred location! 130 | #[allow(deprecated_where_clause_location)] 131 | type R<'a, X> 132 | where 133 | X: 'a, 134 | = &'a X; 135 | } 136 | 137 | fn impls_gat(_: impl Gat) {} 138 | 139 | impls_gat(S); 140 | impls_gat(Box::new(S)); 141 | } 142 | 143 | #[test] 144 | fn custom_deref_target() { 145 | #[autoimpl(Deref, DerefMut using self.0)] 146 | struct BoxingWrapper(Box); 147 | 148 | #[autoimpl(for BoxingWrapper)] 149 | trait Increment { 150 | fn increment(&mut self); 151 | } 152 | 153 | impl Increment for i32 { 154 | fn increment(&mut self) { 155 | *self += 1; 156 | } 157 | } 158 | 159 | let mut x = BoxingWrapper(Box::new(0)); 160 | x.increment(); 161 | assert_eq!(*x.0, 1); 162 | 163 | let mut y = 10; 164 | y.increment(); 165 | let mut z = BoxingWrapper(Box::new(&mut y as &mut dyn Increment)); 166 | z.increment(); 167 | assert_eq!(y, 12); 168 | } 169 | 170 | #[allow(unused)] 171 | #[autoimpl(for &T)] 172 | trait Cfgs { 173 | #[cfg(test)] 174 | fn included(&self); 175 | 176 | #[cfg(mock_feature)] 177 | fn excluded(&self); 178 | } 179 | 180 | #[allow(unused)] 181 | #[autoimpl(for + Debug + ?Sized> U)] 182 | trait SharedData: Debug { 183 | type Item; 184 | fn get(&self, key: &Key) -> Option; 185 | } 186 | -------------------------------------------------------------------------------- /tests/impl_anon.rs: -------------------------------------------------------------------------------- 1 | // Test impl_anon! 2 | 3 | use core::fmt::Debug; 4 | use impl_tools::impl_anon; 5 | 6 | #[test] 7 | fn a() { 8 | let a = impl_anon! { 9 | #[derive(Clone, Debug)] 10 | struct(T = "abc"); 11 | 12 | impl Self { 13 | fn format(&self) -> String { 14 | format!("Anon({:?})", self.0) 15 | } 16 | } 17 | }; 18 | 19 | debug_assert_eq!(a.format(), "Anon(\"abc\")"); 20 | } 21 | 22 | #[test] 23 | fn b() { 24 | let b = impl_anon! { 25 | #[derive(Clone, Debug)] 26 | struct { 27 | t: T = 123, 28 | } 29 | 30 | impl Self { 31 | fn format(&self) -> String { 32 | format!("Anon {{ t: {:?} }}", self.t) 33 | } 34 | } 35 | }; 36 | debug_assert_eq!(b.format(), "Anon { t: 123 }"); 37 | } 38 | -------------------------------------------------------------------------------- /tests/impl_default.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | // Test no_std 3 | #![no_std] 4 | extern crate alloc; 5 | use alloc::string::{String, ToString}; 6 | 7 | use impl_tools::{impl_default, impl_scope}; 8 | 9 | #[impl_default(LR::Left(0))] 10 | #[derive(Debug, PartialEq)] 11 | enum LR { 12 | Left(i32), 13 | Right(i32), 14 | } 15 | 16 | #[test] 17 | fn lr() { 18 | assert_eq!(LR::default(), LR::Left(0)); 19 | } 20 | 21 | #[impl_default(Single::A(Default::default()) where T: Default)] 22 | enum Single { 23 | A(T), 24 | } 25 | 26 | impl_scope! { 27 | #[impl_default(UnsafeNumber { as_u64: 0 })] 28 | union UnsafeNumber { 29 | as_u64: u64, 30 | as_f64: f64, 31 | } 32 | } 33 | 34 | #[test] 35 | fn unsafe_number() { 36 | let un = UnsafeNumber::default(); 37 | unsafe { 38 | let UnsafeNumber { as_f64: v } = un; 39 | assert_eq!(v, 0.0); 40 | } 41 | } 42 | 43 | impl_scope! { 44 | #[impl_default(where T: trait)] 45 | struct Person { 46 | name: String = "Jane Doe".to_string(), 47 | age: u32 = 72, 48 | occupation: T, 49 | } 50 | } 51 | 52 | #[test] 53 | fn person() { 54 | let person = Person::::default(); 55 | assert_eq!(person.name, "Jane Doe"); 56 | assert_eq!(person.age, 72); 57 | assert_eq!(person.occupation, ""); 58 | } 59 | -------------------------------------------------------------------------------- /tests/impl_scope.rs: -------------------------------------------------------------------------------- 1 | // Test impl_scope! 2 | 3 | use core::fmt::Debug; 4 | use impl_tools::impl_scope; 5 | 6 | impl_scope! { 7 | #[derive(Clone, Debug)] 8 | struct A(T); 9 | 10 | impl Self { 11 | fn new(t: T) -> Self { 12 | A(t) 13 | } 14 | } 15 | } 16 | 17 | #[test] 18 | fn a() { 19 | let a = A::new("abc"); 20 | debug_assert_eq!(format!("{a:?}"), "A(\"abc\")"); 21 | } 22 | 23 | impl_scope! { 24 | #[allow(unused)] 25 | #[derive(Clone, Debug)] 26 | struct B { 27 | t: T, 28 | } 29 | 30 | impl Self { 31 | fn new(t: T) -> Self { 32 | B { t } 33 | } 34 | } 35 | } 36 | 37 | #[test] 38 | fn b() { 39 | let b = B::new(123); 40 | debug_assert_eq!(format!("{b:?}"), "B { t: 123 }"); 41 | } 42 | -------------------------------------------------------------------------------- /tests/newtype.rs: -------------------------------------------------------------------------------- 1 | //! Test implementing traits over newtype wrappers 2 | 3 | use std::rc::Rc; 4 | use std::sync::Arc; 5 | 6 | mod inner { 7 | use super::*; 8 | use impl_tools::autoimpl; 9 | 10 | #[autoimpl(for &T, &mut T, Box, Rc, Arc)] 11 | // Optionally, we can also implement Foo directly for these types: 12 | // #[autoimpl(for NewFoo, BoxFoo)] 13 | pub trait Foo { 14 | fn is_true(&self) -> bool; 15 | } 16 | 17 | #[autoimpl(Deref, DerefMut using self.0)] 18 | pub struct NewFoo(T); 19 | impl NewFoo { 20 | pub fn new(foo: T) -> Self { 21 | NewFoo(foo) 22 | } 23 | } 24 | 25 | #[autoimpl(Deref using self.0)] 26 | pub struct FooRef<'a>(&'a dyn Foo); 27 | impl<'a> FooRef<'a> { 28 | pub fn new(foo: &'a dyn Foo) -> Self { 29 | FooRef(foo) 30 | } 31 | } 32 | 33 | #[autoimpl(Deref, DerefMut using self.0)] 34 | pub struct FooRefMut<'a>(&'a mut dyn Foo); 35 | impl<'a> FooRefMut<'a> { 36 | pub fn new(foo: &'a mut dyn Foo) -> Self { 37 | FooRefMut(foo) 38 | } 39 | } 40 | 41 | #[autoimpl(Deref, DerefMut using self.0)] 42 | pub struct BoxFoo(Box); 43 | impl BoxFoo { 44 | pub fn new(foo: Box) -> Self { 45 | BoxFoo(foo) 46 | } 47 | } 48 | 49 | #[autoimpl(Deref, DerefMut using self.0)] 50 | pub struct BoxDynFoo(Box); 51 | impl BoxDynFoo { 52 | pub fn new(foo: Box) -> Self { 53 | BoxDynFoo(foo) 54 | } 55 | } 56 | 57 | #[autoimpl(Deref, DerefMut using self.0)] 58 | pub struct RcDynFoo(Rc); 59 | impl RcDynFoo { 60 | pub fn new(foo: Rc) -> Self { 61 | RcDynFoo(foo) 62 | } 63 | } 64 | 65 | #[autoimpl(Deref, DerefMut using self.0)] 66 | pub struct ArcDynFoo(Arc); 67 | impl ArcDynFoo { 68 | pub fn new(foo: Arc) -> Self { 69 | ArcDynFoo(foo) 70 | } 71 | } 72 | } 73 | 74 | #[derive(Clone, Copy, Default)] 75 | pub struct V(bool); 76 | impl inner::Foo for V { 77 | fn is_true(&self) -> bool { 78 | self.0 79 | } 80 | } 81 | 82 | #[test] 83 | fn newtype() { 84 | use inner::*; 85 | 86 | let mut v = V(true); 87 | 88 | assert!(v.is_true()); 89 | assert!(NewFoo::new(v).is_true()); 90 | assert!(FooRef::new(&v).is_true()); 91 | assert!(FooRefMut::new(&mut v).is_true()); 92 | assert!(BoxFoo::new(Box::new(v)).is_true()); 93 | assert!(BoxDynFoo::new(Box::new(v)).is_true()); 94 | assert!(RcDynFoo::new(Rc::new(v)).is_true()); 95 | assert!(ArcDynFoo::new(Arc::new(v)).is_true()); 96 | } 97 | --------------------------------------------------------------------------------