├── .gitignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── other.md │ ├── feature_request.md │ └── bug_report.md ├── dependabot.yml └── workflows │ └── test.yml ├── COPYRIGHT ├── compile-errors ├── common │ ├── wrong-item-type.rs │ ├── expect-self.stderr │ ├── expect-self.rs │ └── wrong-item-type.stderr ├── stable │ ├── expect-self-anon.stderr │ └── expect-self-anon.rs └── nightly │ ├── expect-self-anon.rs │ ├── trait-autoimpl-with-sized-fn-bound.rs │ ├── expect-self-anon.stderr │ └── trait-autoimpl-with-sized-fn-bound.stderr ├── tests ├── test-cfg │ ├── Cargo.toml │ └── tests │ │ └── autoimpl.rs ├── compile-errors.rs ├── impl_scope.rs ├── impl_self.rs ├── impl_anon.rs ├── impl_default.rs ├── autoimpl_enum.rs ├── newtype.rs ├── for_deref.rs └── autoimpl.rs ├── lib ├── README.md ├── Cargo.toml └── src │ ├── lib.rs │ ├── autoimpl │ ├── impl_using.rs │ └── for_deref.rs │ ├── default.rs │ ├── fields.rs │ ├── anon.rs │ ├── autoimpl.rs │ ├── scope.rs │ └── generics.rs ├── Cargo.toml ├── CHANGELOG.md ├── README.md ├── LICENSE ├── Cargo.lock.msrv └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: dhardy 4 | liberapay: dhardy 5 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | This work is copyrighted by the following contributors: 2 | 3 | Diggory Hardy 4 | 5 | This list may be incomplete. 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /compile-errors/common/wrong-item-type.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | #[impl_tools::autoimpl(Copy)] 4 | union U { 5 | signed: i32, 6 | unsigned: u32, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /compile-errors/stable/expect-self-anon.stderr: -------------------------------------------------------------------------------- 1 | error: expected `Self` or `Trait for Self` 2 | --> compile-errors/stable/expect-self-anon.rs:6:14 3 | | 4 | 6 | impl () { 5 | | ^^ 6 | -------------------------------------------------------------------------------- /compile-errors/common/expect-self.stderr: -------------------------------------------------------------------------------- 1 | error: expected `Self` or `A` or `A<...>` or `Trait for Self`, etc 2 | --> compile-errors/common/expect-self.rs:5:10 3 | | 4 | 5 | impl () { 5 | | ^^ 6 | -------------------------------------------------------------------------------- /compile-errors/common/expect-self.rs: -------------------------------------------------------------------------------- 1 | impl_tools::impl_scope! { 2 | #[derive(Clone, Debug)] 3 | struct A(T); 4 | 5 | impl () { 6 | fn new(t: T) -> A { 7 | A(t) 8 | } 9 | } 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /compile-errors/nightly/expect-self-anon.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _ = impl_tools::impl_anon! { 3 | #[derive(Clone, Debug)] 4 | struct (i32 = 123); 5 | 6 | impl () { 7 | fn get(&self) -> i32 { 8 | self.0 9 | } 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /compile-errors/stable/expect-self-anon.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _ = impl_tools::impl_anon! { 3 | #[derive(Clone, Debug)] 4 | struct (i32 = 123); 5 | 6 | impl () { 7 | fn get(&self) -> i32 { 8 | self.0 9 | } 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /tests/test-cfg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-cfg" 3 | version = "0.1.0" 4 | authors.workspace = true 5 | license.workspace = true 6 | rust-version.workspace = true 7 | edition.workspace = true 8 | publish = false 9 | 10 | [features] 11 | feature1 = [] 12 | 13 | [dependencies] 14 | impl-tools = { version = "0.11", path = "../.." } 15 | -------------------------------------------------------------------------------- /tests/compile-errors.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn compile_errors() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("compile-errors/common/*.rs"); 5 | 6 | #[cfg(feature = "nightly-diagnostics")] 7 | t.compile_fail("compile-errors/nightly/*.rs"); 8 | 9 | #[cfg(not(feature = "nightly-diagnostics"))] 10 | t.compile_fail("compile-errors/stable/*.rs"); 11 | } 12 | -------------------------------------------------------------------------------- /compile-errors/common/wrong-item-type.stderr: -------------------------------------------------------------------------------- 1 | error: #[autoimpl(Trait)] can only be used on enum or struct items 2 | --> compile-errors/common/wrong-item-type.rs:3:1 3 | | 4 | 3 | #[impl_tools::autoimpl(Copy)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `impl_tools::autoimpl` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs: -------------------------------------------------------------------------------- 1 | use impl_tools::autoimpl; 2 | use core::fmt::Debug; 3 | 4 | #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] 5 | trait G 6 | where 7 | V: Debug, 8 | { 9 | fn g(&self) -> V; 10 | 11 | fn s(&self, f: impl Fn(V) -> X) -> X 12 | where 13 | Self: Sized, 14 | { 15 | f(self.g()) 16 | } 17 | } 18 | 19 | fn main() { 20 | compile_error!("Warnings verification"); 21 | } 22 | -------------------------------------------------------------------------------- /compile-errors/nightly/expect-self-anon.stderr: -------------------------------------------------------------------------------- 1 | error: expected `Self` or `Trait for Self` 2 | --> compile-errors/nightly/expect-self-anon.rs:6:14 3 | | 4 | 6 | impl () { 5 | | ^^ 6 | 7 | error: expected expression, found end of macro arguments 8 | --> compile-errors/nightly/expect-self-anon.rs:2:13 9 | | 10 | 2 | let _ = impl_tools::impl_anon! { 11 | | _____________^ 12 | 3 | | #[derive(Clone, Debug)] 13 | 4 | | struct (i32 = 123); 14 | ... | 15 | 11 | | }; 16 | | |_____^ 17 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /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/impl_self.rs: -------------------------------------------------------------------------------- 1 | // Test impl_self! 2 | 3 | use core::fmt::Debug; 4 | use impl_tools::impl_self; 5 | 6 | #[impl_self] 7 | mod A { 8 | #[derive(Clone, Debug)] 9 | struct A(T); 10 | 11 | impl Self { 12 | fn new(t: T) -> Self { 13 | A(t) 14 | } 15 | } 16 | } 17 | 18 | #[test] 19 | fn a() { 20 | let a = A::new("abc"); 21 | debug_assert_eq!(format!("{a:?}"), "A(\"abc\")"); 22 | } 23 | 24 | #[impl_self] 25 | mod B { 26 | #[allow(unused)] 27 | #[derive(Clone, Debug)] 28 | struct B { 29 | t: T, 30 | } 31 | 32 | impl Self { 33 | fn new(t: T) -> Self { 34 | B { t } 35 | } 36 | } 37 | } 38 | 39 | #[test] 40 | fn b() { 41 | let b = B::new(123); 42 | debug_assert_eq!(format!("{b:?}"), "B { t: 123 }"); 43 | } 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "impl-tools-lib" 3 | version = "0.11.4" 4 | description = "Helper macros: autoimpl" 5 | keywords = ["derive", "trait"] 6 | readme = "README.md" 7 | documentation = "https://docs.rs/impl-tools-lib/" 8 | authors.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | rust-version.workspace = true 12 | edition.workspace = true 13 | 14 | [features] 15 | # Squelch lint: "autoimpl on trait that has a method with Self: Sized bound" 16 | allow-trait-autoimpl-with-sized-fn-bound = [] 17 | 18 | [dependencies] 19 | quote = "1.0" 20 | proc-macro2 = "1.0" 21 | 22 | [dependencies.proc-macro-error2] 23 | version = "2.0" 24 | default-features = false 25 | 26 | [dependencies.syn] 27 | version = "2.0.0" 28 | # We need 'extra-traits' for equality testing 29 | # We need 'full' for parsing macros within macro arguments 30 | features = ["extra-traits", "full", "visit", "visit-mut"] 31 | 32 | [lints.clippy] 33 | needless_lifetimes = "allow" 34 | -------------------------------------------------------------------------------- /compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.stderr: -------------------------------------------------------------------------------- 1 | warning: autoimpl on trait that has a method with Self: Sized bound 2 | --> compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs:4:1 3 | | 4 | 4 | #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | note: method impl uses default implementation, not deref 8 | --> compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs:11:5 9 | | 10 | 11 | / fn s(&self, f: impl Fn(V) -> X) -> X 11 | 12 | | where 12 | 13 | | Self: Sized, 13 | ... | 14 | 16 | | } 15 | | |_____^ 16 | = note: this warning originates in the attribute macro `autoimpl` (in Nightly builds, run with -Z macro-backtrace for more info) 17 | 18 | error: Warnings verification 19 | --> compile-errors/nightly/trait-autoimpl-with-sized-fn-bound.rs:20:5 20 | | 21 | 20 | compile_error!("Warnings verification"); 22 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["lib", "tests/test-cfg"] 3 | 4 | [workspace.package] 5 | authors = ["Diggory Hardy "] 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/kas-gui/impl-tools" 8 | rust-version = "1.70.0" 9 | edition = "2021" 10 | 11 | [package] 12 | name = "impl-tools" 13 | version = "0.11.4" 14 | description = "Helper macros: autoimpl" 15 | keywords = ["proc-macro", "macro", "derive", "trait", "procedural"] 16 | categories = ["development-tools::procedural-macro-helpers"] 17 | readme = "README.md" 18 | documentation = "https://docs.rs/impl-tools/" 19 | authors.workspace = true 20 | license.workspace = true 21 | repository.workspace = true 22 | rust-version.workspace = true 23 | edition.workspace = true 24 | 25 | [lib] 26 | proc-macro = true 27 | 28 | [features] 29 | # Enables better proc-macro diagnostics (including warnings); nightly only. 30 | nightly-diagnostics = ["proc-macro-error2/nightly"] 31 | # Squelch lint: "autoimpl on trait that has a method with Self: Sized bound" 32 | allow-trait-autoimpl-with-sized-fn-bound = ["impl-tools-lib/allow-trait-autoimpl-with-sized-fn-bound"] 33 | 34 | [dependencies.proc-macro-error2] 35 | version = "2.0" 36 | default-features = false 37 | 38 | [dependencies.syn] 39 | version = "2.0.0" 40 | 41 | [dependencies.impl-tools-lib] 42 | version = "0.11.4" 43 | path = "lib" 44 | 45 | [dev-dependencies] 46 | doc-comment = "0.3.3" 47 | twox-hash = "1.6.3" 48 | trybuild = "1.0.90" 49 | 50 | [build-dependencies] 51 | autocfg = "1.1.0" 52 | 53 | [lints.rust] 54 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(mock_feature)'] } 55 | -------------------------------------------------------------------------------- /tests/test-cfg/tests/autoimpl.rs: -------------------------------------------------------------------------------- 1 | //! Test that autoimpl can handle cfg on fields 2 | 3 | #![allow(non_snake_case)] 4 | 5 | use impl_tools::autoimpl; 6 | 7 | #[autoimpl(Clone, Debug, Default where T: trait)] 8 | #[derive(PartialEq, Eq)] 9 | struct S { 10 | a: T, 11 | #[cfg(unix)] 12 | b: String, 13 | #[cfg(not(unix))] 14 | b: Box, 15 | #[cfg(feature = "feature1")] 16 | c: Vec, 17 | } 18 | 19 | impl S { 20 | fn new(a: T) -> Self { 21 | S { 22 | a, 23 | b: "hello world".to_string().into(), 24 | #[cfg(feature = "feature1")] 25 | c: vec![1, 1, 2, 3, 5, 8], 26 | } 27 | } 28 | } 29 | 30 | #[test] 31 | fn test_clone_S() { 32 | let a = S::new(()); 33 | assert_eq!(a.clone(), a); 34 | assert!(a != S::default()); 35 | } 36 | 37 | #[test] 38 | fn test_debug_S() { 39 | let s = S::new(42); 40 | #[cfg(not(feature = "feature1"))] 41 | let expected = "S { a: 42, b: \"hello world\" }"; 42 | #[cfg(feature = "feature1")] 43 | let expected = "S { a: 42, b: \"hello world\", c: [1, 1, 2, 3, 5, 8] }"; 44 | assert_eq!(format!("{s:?}"), expected); 45 | } 46 | 47 | #[autoimpl(Clone, Debug where T: trait)] 48 | #[derive(PartialEq, Eq)] 49 | enum E { 50 | A(T), 51 | #[cfg(unix)] 52 | B(String), 53 | #[cfg(not(unix))] 54 | B(Box), 55 | C { 56 | #[cfg(feature = "feature1")] 57 | nums: Vec, 58 | }, 59 | } 60 | 61 | #[test] 62 | fn test_clone_E() { 63 | let a = E::A(2.2); 64 | assert_eq!(a.clone(), a); 65 | 66 | let b = E::<()>::B("rain".to_string().into()); 67 | assert_eq!(b.clone(), b); 68 | 69 | let c = E::<()>::C { 70 | #[cfg(feature = "feature1")] 71 | nums: vec![1, 2, 3], 72 | }; 73 | assert_eq!(c.clone(), c); 74 | } 75 | -------------------------------------------------------------------------------- /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 | #[autoimpl(std::clone::Clone)] 32 | #[allow(unused)] 33 | struct NotCopy(i32); 34 | 35 | #[autoimpl(std::clone::Clone)] 36 | #[allow(unused)] 37 | enum CloneNotCopy { 38 | A { not_copy: NotCopy }, 39 | } 40 | 41 | #[test] 42 | fn variants() { 43 | test_has_copy(Variants::A); 44 | test_has_clone(Variants::A); 45 | test_has_clone(CloneNotCopy::A { 46 | not_copy: NotCopy(0), 47 | }); 48 | assert_eq!(format!("{:?}", Variants::A), "Variants::A"); 49 | assert_eq!(format!("{:?}", Variants::B(())), "Variants::B(())"); 50 | assert_eq!( 51 | format!("{:?}", Variants::C("abc", 3)), 52 | "Variants::C(\"abc\", 3)" 53 | ); 54 | assert_eq!( 55 | format!("{:?}", Variants::D { x: 3, y: 2 }), 56 | "Variants::D { x: 3, y: 2 }" 57 | ); 58 | } 59 | 60 | #[autoimpl(Copy, Clone, Debug where T: trait)] 61 | #[impl_default(MyOption::None)] 62 | #[autoimpl(PartialEq, Eq where T: trait)] 63 | enum MyOption { 64 | None, 65 | Some(T), 66 | } 67 | 68 | #[test] 69 | fn my_option() { 70 | test_has_clone(MyOption::::default()); 71 | test_has_copy(MyOption::Some(1)); 72 | assert_eq!(format!("{:?}", MyOption::Some(1)), "MyOption::Some(1)"); 73 | } 74 | -------------------------------------------------------------------------------- /.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 | test: 11 | name: Test 12 | strategy: 13 | matrix: 14 | include: 15 | - os: ubuntu-latest 16 | toolchain: nightly 17 | variant: docs 18 | features: "--features nightly-diagnostics" 19 | - os: macos-latest 20 | toolchain: beta 21 | - os: windows-latest 22 | toolchain: stable 23 | - os: ubuntu-latest 24 | toolchain: "1.70.0" 25 | targets: "--lib --tests" 26 | variant: MSRV 27 | 28 | runs-on: ${{ matrix.os }} 29 | 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v6 33 | 34 | - name: MSRV 35 | if: ${{ matrix.variant == 'MSRV' }} 36 | run: cp Cargo.lock.msrv Cargo.lock 37 | 38 | - name: Install toolchain 39 | uses: dtolnay/rust-toolchain@master 40 | with: 41 | toolchain: ${{ matrix.toolchain }} 42 | 43 | - name: Build docs 44 | if: ${{ matrix.variant == 'docs' }} 45 | run: cargo doc ${{ matrix.features }} --all --no-deps 46 | 47 | - name: Test impl-tools-lib 48 | run: cargo test --manifest-path lib/Cargo.toml ${{ matrix.targets }} 49 | 50 | - name: Test impl-tools 51 | run: cargo test ${{ matrix.features }} ${{ matrix.targets }} 52 | 53 | - name: Test test-cfg 54 | working-directory: tests/test-cfg 55 | run: | 56 | cargo test 57 | cargo test --features feature1 58 | 59 | workspace: 60 | runs-on: ubuntu-latest 61 | steps: 62 | - uses: actions/checkout@v6 63 | - uses: dtolnay/rust-toolchain@master 64 | with: 65 | toolchain: stable 66 | components: rustfmt,clippy 67 | - name: rustfmt 68 | run: cargo fmt --all -- --check 69 | - name: clippy 70 | run: cargo clippy --workspace -- -D warnings 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | #[allow(unused)] 15 | const A: i32; 16 | 17 | fn f(&self); 18 | fn g(&mut self, a: i32, b: &Self::B); 19 | 20 | type B; 21 | } 22 | 23 | impl Z for () { 24 | const A: i32 = 10; 25 | 26 | fn f(&self) {} 27 | fn g(&mut self, _: i32, _: &i32) {} 28 | 29 | type B = i32; 30 | } 31 | 32 | #[test] 33 | fn z() { 34 | fn impls_z(mut z: impl Z) { 35 | z.f(); 36 | z.g(1, &2); 37 | } 38 | 39 | impls_z(()); 40 | impls_z(&mut ()); 41 | impls_z(Box::new(())); 42 | } 43 | 44 | macro_rules! ident { 45 | ($t:tt) => { 46 | $t 47 | }; 48 | } 49 | 50 | #[autoimpl(for<'a, T> &'a mut T where T: trait + ?Sized)] 51 | pub trait BitRead2 { 52 | fn copy_to(&mut self, mut _n: u64) -> Result<(), core::convert::Infallible> { 53 | Ok(()) 54 | } 55 | 56 | fn p(&self, ident!(_x): i32); 57 | 58 | #[allow(unused_parens)] 59 | fn fn_with_parens(&self, (_x): i32) {} 60 | 61 | fn fn_with_path(&self, core::any::TypeId { .. }: core::any::TypeId) {} 62 | } 63 | 64 | #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] 65 | trait G 66 | where 67 | V: Debug, 68 | { 69 | fn g(&self) -> V; 70 | } 71 | 72 | #[test] 73 | fn g() { 74 | struct S; 75 | impl G for S { 76 | fn g(&self) -> i32 { 77 | 123 78 | } 79 | } 80 | 81 | fn impls_g(g: impl G) { 82 | assert_eq!(g.g(), 123); 83 | } 84 | 85 | impls_g(S); 86 | impls_g(&S); 87 | impls_g(&&S); 88 | impls_g(&mut S); 89 | impls_g(&&mut S); 90 | impls_g(&S as &dyn G); 91 | impls_g(Box::new(S)); 92 | impls_g(&mut &Box::new(S)); 93 | impls_g(Box::new(S) as Box>); 94 | impls_g(&mut (Box::new(S) as Box>)); 95 | } 96 | 97 | #[allow(unused)] 98 | #[autoimpl(for &T)] 99 | trait H> 100 | where 101 | X: Debug, 102 | { 103 | } 104 | 105 | #[autoimpl(for Box)] 106 | trait Gat { 107 | type T; 108 | 109 | type R<'a, X>: core::ops::Deref> 110 | where 111 | X: 'a; 112 | } 113 | 114 | #[test] 115 | fn gat() { 116 | struct S; 117 | impl Gat for S { 118 | type T = X; 119 | 120 | // Our MSRV doesn't support the preferred location! 121 | #[allow(deprecated_where_clause_location)] 122 | type R<'a, X> 123 | where 124 | X: 'a, 125 | = &'a X; 126 | } 127 | 128 | fn impls_gat(_: impl Gat) {} 129 | 130 | impls_gat(S); 131 | impls_gat(Box::new(S)); 132 | } 133 | 134 | #[test] 135 | fn custom_deref_target() { 136 | #[autoimpl(Deref, DerefMut using self.0)] 137 | struct BoxingWrapper(Box); 138 | 139 | #[autoimpl(for BoxingWrapper)] 140 | trait Increment { 141 | fn increment(&mut self); 142 | } 143 | 144 | impl Increment for i32 { 145 | fn increment(&mut self) { 146 | *self += 1; 147 | } 148 | } 149 | 150 | let mut x = BoxingWrapper(Box::new(0)); 151 | x.increment(); 152 | assert_eq!(*x.0, 1); 153 | 154 | let mut y = 10; 155 | y.increment(); 156 | let mut z = BoxingWrapper(Box::new(&mut y as &mut dyn Increment)); 157 | z.increment(); 158 | assert_eq!(y, 12); 159 | } 160 | 161 | #[allow(unused)] 162 | #[autoimpl(for &T)] 163 | trait Cfgs { 164 | #[cfg(test)] 165 | fn included(&self); 166 | 167 | #[cfg(mock_feature)] 168 | fn excluded(&self); 169 | } 170 | 171 | #[allow(unused)] 172 | #[autoimpl(for + Debug + ?Sized> U)] 173 | trait SharedData: Debug { 174 | type Item; 175 | fn get(&self, key: &Key) -> Option; 176 | } 177 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.11.4] — unreleased 6 | 7 | - Bump MSRV to 1.70 (#59) 8 | - Add feature `nightly-diagnostics` (#60) 9 | - Warn on usage of `#[autoimpl]` for traits where a method has a `Self: Sized` bound, with opt-out feature `allow-trait-autoimpl-with-sized-fn-bound` (#60) 10 | - Let `#[autoimpl(Clone, Debug, Default)]` support `#[cfg]` on struct fields, enum variants and variant fields (#59) 11 | - Let `#[autoimpl(Clone, Debug)]` support `ignore` on named enum fields (#59) 12 | 13 | # [0.11.3] (lib only) 14 | 15 | - Fix `#[autoimpl(Clone)]` and `#[autoimpl(Hash)]` for non-`Copy` enums (#55, #56) 16 | 17 | # [0.11.2] 18 | 19 | Version numbers are re-synchronised. In addition: 20 | 21 | - Bump MSRV to 1.65 (#51) 22 | - Add `scope::{ScopeModAttrs, ScopeMod}` to `impl-tools-lib` (#53) 23 | - Add `#[impl_self]` to `impl-tools` (#53) 24 | 25 | ## [0.10.3], [impl-tools-lib-0.11.1] — 2024-12-21 26 | 27 | - Let `#[autoimpl]` on traits support function arguments using `mut` and destructuring patterns (#45) 28 | - Improve documentation for `#[autoimpl]` (#46) 29 | 30 | ## [0.10.2], [impl-tools-lib-0.11.0] — 2024-12-09 31 | 32 | 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] 33 | "). 34 | 35 | `impl-tools` is unaffected excepting where an incompatible version of `impl-tools-lib` is used. Broken versions will be yanked. 36 | 37 | ## [0.10.1] — 2024-10-21 38 | 39 | - Improve CI workflows (#38) 40 | - Explicit MSRV = 1.58 (#38) 41 | - Replace dependency `proc-macro-error` with `proc-macro-error2` (#41) 42 | - Bump MSRV to 1.61 (#42) 43 | 44 | ## [0.10.0] — 2023-09-07 45 | 46 | - Rename `singleton!` → `impl_anon!` (#36) 47 | - Reorganise `impl-tools-lib`: new `anon` and `scope` public modules (#36) 48 | 49 | ## [0.9.1] — 2023-09-07 50 | 51 | - Fix clone for fields which auto-deref (issue #34) 52 | 53 | ## [0.9.0] — 2023-06-28 54 | 55 | - Update to syn v2.0.0 56 | 57 | ## [0.8.0] — 2023-02-07 58 | 59 | - Bump MSRV to 1.58.0 (#31) 60 | - `#[autoimpl(Clone, Debug, PartialEq, Eq, Hash)]` now all support enums 61 | (with optional `where` clause, without `ignore` clauses) (#31) 62 | - Add `impl_tools_lib::ImplTrait::enum_impl`, `enum_items` with default impls; 63 | `ImplTraits::expand` now supports enums (#31) 64 | - Add `impl_tools_lib::Ident_formatter` utility (#31) 65 | 66 | Note: `PartialOrd, Ord` *could* now support enums (unimplemented). `ignore` and 67 | `using` clauses are deliberately not supported (due to syntactic ambiguity). 68 | 69 | ## [0.6.2], `impl-tools-lib` [0.7.1] — 2022-12-16 70 | 71 | - Fix `#[autoimpl]` on traits: copy `#[cfg(..)]` attributes (#30) 72 | 73 | ## [0.6.1], `impl-tools-lib` [0.7.0] — 2022-12-01 74 | 75 | - Better diagnostics for trait-redefinition: require `Deref` bound (#28) 76 | - Document `Deref` with custom `Target` type 77 | 78 | `impl-tools-lib` has breaking changes and therefore a higher version number: 79 | 80 | - Replace free function `impl_generics` with method `Generics::impl_generics` 81 | - Add method `Generics::ty_generics` 82 | 83 | Note: next breaking release for `impl-tools` should bump version to match `-lib`. 84 | 85 | ## [0.6.0] — 2022-11-17 86 | 87 | - Add `ImplTrait::support_path_args`, `ImplArgs::path_args` (#26) 88 | - Path args: support `Deref` (#26) 89 | 90 | ## [0.5.2] — 2022-10-06 91 | 92 | - Add `singleton!` macro (#25) 93 | 94 | ## [0.5.1] — 2022-09-23 95 | 96 | - Fix: do not copy attributes on trait items (#24) 97 | 98 | ## [0.5.0] — 2022-09-22 99 | 100 | - `#[autoimpl]` on traits now merges trait generics with macro generics (#21) 101 | - `lib::autoimpl::struct_items` returns the trait path in addition to impl items (#22) 102 | - Add `lib::autoimpl::ImplArgs::for_fields`, `for_fields_iter` (#22) 103 | - Add autoimpl support for `Copy`, `AsRef`, `AsMut`, `Borrow`, `BorrowMut`, 104 | `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash` (#22) 105 | - Add `#[automatically_derived]` annotation to generated impls (#22) 106 | 107 | ## [0.4.4] — 2022-09-19 108 | 109 | - Fix `#[autoimpl]` on traits for items with where bounds on `Self` (#20) 110 | 111 | ## [0.4.3] — 2022-09-17 112 | 113 | - Fix `#[autoimpl]` on traits for GATs with where clauses (#19) 114 | 115 | ## [0.4.2] — 2022-09-17 116 | 117 | - Correct release of 0.4.1 (which bumped the version of impl-tools without 118 | bumping impl-tools-lib) (#18) 119 | - Fix `#[autoimpl]` on traits for GATs and attributes on trait const/method/type items (#17) 120 | 121 | ## [0.4.1] — 2022-09-17 122 | 123 | No changes (prefer 0.4.2 instead). 124 | 125 | ## [0.4.0] — 2022-08-19 126 | 127 | Change signature of `ScopeAttr::apply`: replace `args: TokenStream, attr_span: Span` 128 | with `attr: Attribute` (#15). 129 | 130 | ## [0.3.2] — 2022-06-01 131 | 132 | Support `no_std`. Support matching standard traits via paths from `core`/`alloc` 133 | as well as via paths from `std`. 134 | 135 | ## [0.3.1] — 2022-04-17 136 | 137 | Documentation improvements only. 138 | 139 | ## [0.3.0] — 2022-03-29 140 | 141 | The library now supports extensibility. Most code has been moved to a new crate, 142 | `impl-tools-lib`. Users may replace `impl-tools` with their own front-end 143 | proc-macro crate, adding/removing the traits supported by `#[autoimpl]` and the 144 | attributes supported by `impl_scope!`. 145 | 146 | - Extensibility for `impl_scope!` 147 | - Extensibility for `#[autoimpl]` 148 | - Permit path arguments in `#[autoimpl]` 149 | - Bug-fix for `#[autoimpl(Debug)]` on tuple and unit structs 150 | - Lots of internal code revision 151 | 152 | ## [0.2.0] — 2022-03-23 153 | 154 | Add `impl_scope!` function-like macro (derived from `kas_macros::widget!`) and 155 | `#[impl_default]` attribute macro. 156 | 157 | ## [0.1.0] — 2022-03-21 158 | 159 | New release, including the `#[autoimpl]` attribute macro (extracted from 160 | `kas-macros` crate). 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 8 | A set of helper macros 9 | 10 | 11 | Macros 12 | ------ 13 | 14 | ### Autoimpl 15 | 16 | `#[autoimpl]` is a partial replacement for `#[derive]`, supporting: 17 | 18 | - Explicit `where` clause on generic parameters 19 | - No implicit bounds on generic parameters beyond those required by the type 20 | - Traits like `Deref` by `using` a named field 21 | - Traits like `Debug` may `ignore` named fields 22 | 23 | `#[autoimpl]` may also be used on trait definitions to impl for specified types 24 | supporting `Deref`. 25 | 26 | Unlike [alternatives](#alternatives), `#[autoimpl]` has minimal and intuitive syntax. 27 | 28 | ```rust 29 | use impl_tools::autoimpl; 30 | use std::fmt::Debug; 31 | 32 | // Impl Animal for Box where T: Animal + ?Sized 33 | #[autoimpl(for Box)] 34 | trait Animal { 35 | fn number_of_legs(&self) -> u32; 36 | } 37 | 38 | // Impl Debug for Named omitting field animal from output 39 | #[autoimpl(Debug ignore self.animal where T: Debug)] 40 | // Impl Deref and DerefMut to field animal for Named 41 | #[autoimpl(Deref, DerefMut using self.animal)] 42 | struct Named { 43 | name: T, 44 | animal: A, 45 | } 46 | 47 | fn main() { 48 | struct Fish; 49 | impl Animal for Fish { 50 | fn number_of_legs(&self) -> u32 { 51 | 0 52 | } 53 | } 54 | 55 | let my_fish = Named { 56 | name: "Nemo", 57 | animal: Box::new(Fish), 58 | }; 59 | 60 | assert_eq!( 61 | format!("{my_fish:?} has {} legs!", my_fish.number_of_legs()), 62 | r#"Named { name: "Nemo", .. } has 0 legs!"# 63 | ); 64 | } 65 | ``` 66 | 67 | #### New-type wrappers 68 | 69 | A combination of `Deref` on the new-type and trait-reimplementation on the 70 | trait allows succinct new-type patterns: 71 | 72 | ```rust 73 | use impl_tools::autoimpl; 74 | use std::sync::Arc; 75 | 76 | // Impl Foo for &T, &mut T and Arc 77 | #[autoimpl(for &T, &mut T, Arc)] 78 | // Optional: impl Foo for NewFoo (requires NewFoo: Deref) 79 | #[autoimpl(for NewFoo)] 80 | pub trait Foo { 81 | fn success(&self) -> bool; 82 | } 83 | 84 | // Impl Deref and DerefMut to a Target which itself supports Foo 85 | #[autoimpl(Deref, DerefMut using self.0)] 86 | pub struct NewFoo(T); 87 | 88 | // Impl Deref and DerefMut to a Target which itself supports Foo 89 | #[autoimpl(Deref, DerefMut using self.0)] 90 | pub struct ArcDynFoo(Arc); 91 | 92 | #[test] 93 | fn test_foo_newtypes() { 94 | struct Success; 95 | impl Foo for Success { 96 | fn success(&self) -> bool { true } 97 | } 98 | 99 | // We can now directly call Foo's methods on the wrapper: 100 | assert!(NewFoo(Success).success()); 101 | assert!(ArcDynFoo(Arc::new(Success)).success()); 102 | } 103 | ``` 104 | 105 | See [`tests/newtype.rs`](https://github.com/kas-gui/impl-tools/blob/master/tests/newtype.rs) for more variants of this pattern. 106 | 107 | 108 | ### Impl Default 109 | 110 | `#[impl_default]` implements `std::default::Default`: 111 | 112 | ```rust 113 | #[impl_tools::impl_default(Tree::Ash)] 114 | enum Tree { Ash, Beech, Birch, Willow } 115 | 116 | impl_tools::impl_scope! { 117 | #[impl_default] 118 | struct Copse { 119 | tree_type: Tree, 120 | number: u32 = 7, 121 | } 122 | } 123 | ``` 124 | 125 | Note: `#[impl_default]` is matched within an `impl_scope!` regardless of imports. 126 | 127 | ### Impl Self 128 | 129 | `#[impl_self]` provides `impl Self` syntax, avoiding the 130 | need to repeat generics when writing impls on a local type definition. 131 | This supercedes `impl_scope!` (except regarding `macro@impl_default`). 132 | 133 | ```rust 134 | use std::fmt::Display; 135 | 136 | #[impl_tools::impl_self] 137 | mod NamedThing { 138 | /// I don't know why this exists 139 | pub struct NamedThing { 140 | name: T, 141 | func: F, 142 | } 143 | 144 | // Repeats generic parameters of type 145 | impl Self { 146 | fn format_name(&self) -> String { 147 | format!("{}", self.name) 148 | } 149 | } 150 | 151 | // Merges generic parameters of type 152 | impl Self where F: Fn(&str) -> O { 153 | fn invoke(&self) -> O { 154 | (self.func)(&self.format_name()) 155 | } 156 | } 157 | } 158 | ``` 159 | 160 | Note that `struct NamedThing` is defined directly within the outer namespace, 161 | not within the `mod NamedThing`. This is a hack required to ensure the contents 162 | use valid Rust syntax and are thus formattable using `cargo fmt`. 163 | 164 | ### Impl Anon 165 | 166 | `impl_anon!` is a function-like macro to construct a single-use struct with 167 | custom implementations (similar: [RFC#2604](https://github.com/rust-lang/rfcs/pull/2604)). 168 | 169 | Example: 170 | ```rust 171 | use std::fmt; 172 | fn main() { 173 | let world = "world"; 174 | let says_hello_world = impl_tools::impl_anon! { 175 | struct(&'static str = world); 176 | impl fmt::Display for Self { 177 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 178 | write!(f, "hello {}", self.0) 179 | } 180 | } 181 | }; 182 | assert_eq!(format!("{}", says_hello_world), "hello world"); 183 | } 184 | ``` 185 | 186 | 187 | Extensibility 188 | ------------- 189 | 190 | Rust's `#[derive]` macro is extensible via `#[proc_macro_derive]` in a `proc-macro` crate. 191 | Our macros cannot be extended in the same way, but they can be extended via a new front-end: 192 | 193 | 1. Create a copy of the `impl-tools` crate to create a new "front-end" (`proc-macro` crate). 194 | This crate is contains only a little code over the [`impl-tools-lib`] crate. 195 | 2. To extend `#[autoimpl]`, write an impl of [`ImplTrait`] and add it to the attribute's definition. 196 | To extend `#[impl_self]`, write an impl of [`ScopeAttr`] and add it to the macro's definition. 197 | 3. Depend on your new front end crate instead of `impl-tools`. 198 | 199 | For an example of this approach, see [kas-macros](https://github.com/kas-gui/kas/tree/master/crates/kas-macros). 200 | 201 | [`impl-tools-lib`]: https://docs.rs/impl-tools-lib/ 202 | [`ImplTrait`]: https://docs.rs/impl-tools-lib/latest/impl_tools_lib/autoimpl/trait.ImplTrait.html 203 | [`ScopeAttr`]: https://docs.rs/impl-tools-lib/latest/impl_tools_lib/trait.ScopeAttr.html 204 | 205 | 206 | Alternatives 207 | ------------ 208 | 209 | ### Derive alternatives 210 | 211 | Both [Educe](https://crates.io/crates/educe) and [Derivative](https://crates.io/crates/derivative) 212 | have similar functionality: the ability to implement standard traits with more flexibility than 213 | libstd's `#[derive]`. 214 | 215 | In comparison, impl-tools' `#[autoimpl]` has cleaner syntax but is less flexible: 216 | ```rust,ignore 217 | #[derive(Derivative)] 218 | #[derivative(PartialEq, Eq)] 219 | struct Foo { 220 | foo: S, 221 | #[derivative(PartialEq="ignore")] 222 | bar: u8, 223 | #[derivative(PartialEq(bound=""), Eq(bound=""))] 224 | ptr: *const T, 225 | } 226 | 227 | #[derive(Educe)] 228 | #[educe(PartialEq(bound = "S: PartialEq"), Eq(bound = "S: Eq"))] 229 | struct Foo { 230 | foo: S, 231 | #[educe(PartialEq(ignore))] 232 | bar: u8, 233 | ptr: *const T, 234 | } 235 | 236 | // impl-tools: 237 | #[autoimpl(PartialEq, Eq ignore self.bar where S: trait)] 238 | struct Foo { 239 | foo: S, 240 | bar: u8, 241 | ptr: *const T, 242 | } 243 | ``` 244 | 245 | Note: `#[derive]` and `Derivative` add bounds like `S: PartialEq, T: PartialEq` on generic parameters by default; `Educe` and `impl-tools` do not. 246 | 247 | ### Derive extensions 248 | 249 | [derive_more](https://crates.io/crates/derive_more) isn't exactly an "alternative", simply 250 | supporting `#[derive]` for more standard traits such as `Add` and `From`. 251 | This is not (currently) supported by `#[autoimpl]` (or, to my knowledge, any alternative). 252 | 253 | [auto_impl](https://crates.io/crates/auto_impl/) allows implementing a trait for reference types 254 | (`&`, `&mut`, `Box`, `Rc`, `Arc`) as well as function types. The former (reference types) is 255 | supported by `#[autoimpl]` (and is slightly more general): 256 | ```rust,ignore 257 | // auto_impl: 258 | #[auto_impl(&, Box)] 259 | trait Foo { 260 | fn foo(&self); 261 | } 262 | 263 | // impl-tools: 264 | #[autoimpl(for &T, Box)] 265 | trait Foo { 266 | fn foo(&self); 267 | } 268 | ``` 269 | 270 | [derive-where](https://crates.io/crates/derive-where) is a variant of the 271 | standard `#[derive]` macro supporting custom generic bounds. 272 | (This offers a subset of the functionality of `#[autoimpl]`). 273 | 274 | 275 | Copyright and Licence 276 | --------------------- 277 | 278 | The [COPYRIGHT](COPYRIGHT) file includes a list of contributors who claim 279 | copyright on this project. This list may be incomplete; new contributors may 280 | optionally add themselves to this list. 281 | 282 | The impl-tools library is published under the terms of the Apache License, Version 2.0. 283 | You may obtain a copy of this licence from the [LICENSE](LICENSE) file or on 284 | the following webpage: 285 | 286 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.4.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 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 = "equivalent" 25 | version = "1.0.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 28 | 29 | [[package]] 30 | name = "getrandom" 31 | version = "0.2.16" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 34 | dependencies = [ 35 | "cfg-if", 36 | "libc", 37 | "wasi", 38 | ] 39 | 40 | [[package]] 41 | name = "glob" 42 | version = "0.3.2" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 45 | 46 | [[package]] 47 | name = "hashbrown" 48 | version = "0.15.4" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 51 | 52 | [[package]] 53 | name = "impl-tools" 54 | version = "0.10.3" 55 | dependencies = [ 56 | "autocfg", 57 | "doc-comment", 58 | "impl-tools-lib", 59 | "proc-macro-error2", 60 | "syn", 61 | "trybuild", 62 | "twox-hash", 63 | ] 64 | 65 | [[package]] 66 | name = "impl-tools-lib" 67 | version = "0.11.1" 68 | dependencies = [ 69 | "proc-macro-error2", 70 | "proc-macro2", 71 | "quote", 72 | "syn", 73 | ] 74 | 75 | [[package]] 76 | name = "indexmap" 77 | version = "2.9.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 80 | dependencies = [ 81 | "equivalent", 82 | "hashbrown", 83 | ] 84 | 85 | [[package]] 86 | name = "itoa" 87 | version = "1.0.15" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 90 | 91 | [[package]] 92 | name = "libc" 93 | version = "0.2.172" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 96 | 97 | [[package]] 98 | name = "memchr" 99 | version = "2.7.4" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 102 | 103 | [[package]] 104 | name = "once_cell" 105 | version = "1.21.3" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 108 | 109 | [[package]] 110 | name = "ppv-lite86" 111 | version = "0.2.21" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 114 | dependencies = [ 115 | "zerocopy", 116 | ] 117 | 118 | [[package]] 119 | name = "proc-macro-error-attr2" 120 | version = "2.0.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 123 | dependencies = [ 124 | "proc-macro2", 125 | "quote", 126 | ] 127 | 128 | [[package]] 129 | name = "proc-macro-error2" 130 | version = "2.0.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 133 | dependencies = [ 134 | "proc-macro-error-attr2", 135 | "proc-macro2", 136 | "quote", 137 | ] 138 | 139 | [[package]] 140 | name = "proc-macro2" 141 | version = "1.0.95" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 144 | dependencies = [ 145 | "unicode-ident", 146 | ] 147 | 148 | [[package]] 149 | name = "quote" 150 | version = "1.0.40" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 153 | dependencies = [ 154 | "proc-macro2", 155 | ] 156 | 157 | [[package]] 158 | name = "rand" 159 | version = "0.8.5" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 162 | dependencies = [ 163 | "libc", 164 | "rand_chacha", 165 | "rand_core", 166 | ] 167 | 168 | [[package]] 169 | name = "rand_chacha" 170 | version = "0.3.1" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 173 | dependencies = [ 174 | "ppv-lite86", 175 | "rand_core", 176 | ] 177 | 178 | [[package]] 179 | name = "rand_core" 180 | version = "0.6.4" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 183 | dependencies = [ 184 | "getrandom", 185 | ] 186 | 187 | [[package]] 188 | name = "ryu" 189 | version = "1.0.20" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 192 | 193 | [[package]] 194 | name = "serde" 195 | version = "1.0.219" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 198 | dependencies = [ 199 | "serde_derive", 200 | ] 201 | 202 | [[package]] 203 | name = "serde_derive" 204 | version = "1.0.219" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 207 | dependencies = [ 208 | "proc-macro2", 209 | "quote", 210 | "syn", 211 | ] 212 | 213 | [[package]] 214 | name = "serde_json" 215 | version = "1.0.140" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 218 | dependencies = [ 219 | "itoa", 220 | "memchr", 221 | "ryu", 222 | "serde", 223 | ] 224 | 225 | [[package]] 226 | name = "serde_spanned" 227 | version = "0.6.8" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 230 | dependencies = [ 231 | "serde", 232 | ] 233 | 234 | [[package]] 235 | name = "static_assertions" 236 | version = "1.1.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 239 | 240 | [[package]] 241 | name = "syn" 242 | version = "2.0.102" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" 245 | dependencies = [ 246 | "proc-macro2", 247 | "quote", 248 | "unicode-ident", 249 | ] 250 | 251 | [[package]] 252 | name = "termcolor" 253 | version = "1.4.1" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 256 | dependencies = [ 257 | "winapi-util", 258 | ] 259 | 260 | [[package]] 261 | name = "toml" 262 | version = "0.8.20" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" 265 | dependencies = [ 266 | "serde", 267 | "serde_spanned", 268 | "toml_datetime", 269 | "toml_edit", 270 | ] 271 | 272 | [[package]] 273 | name = "toml_datetime" 274 | version = "0.6.8" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 277 | dependencies = [ 278 | "serde", 279 | ] 280 | 281 | [[package]] 282 | name = "toml_edit" 283 | version = "0.22.23" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" 286 | dependencies = [ 287 | "indexmap", 288 | "serde", 289 | "serde_spanned", 290 | "toml_datetime", 291 | "winnow", 292 | ] 293 | 294 | [[package]] 295 | name = "trybuild" 296 | version = "1.0.90" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "2aa6f84ec205ebf87fb7a0abdbcd1467fa5af0e86878eb6d888b78ecbb10b6d5" 299 | dependencies = [ 300 | "glob", 301 | "once_cell", 302 | "serde", 303 | "serde_derive", 304 | "serde_json", 305 | "termcolor", 306 | "toml", 307 | ] 308 | 309 | [[package]] 310 | name = "twox-hash" 311 | version = "1.6.3" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 314 | dependencies = [ 315 | "cfg-if", 316 | "rand", 317 | "static_assertions", 318 | ] 319 | 320 | [[package]] 321 | name = "unicode-ident" 322 | version = "1.0.18" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 325 | 326 | [[package]] 327 | name = "wasi" 328 | version = "0.11.1+wasi-snapshot-preview1" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 331 | 332 | [[package]] 333 | name = "winapi-util" 334 | version = "0.1.9" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 337 | dependencies = [ 338 | "windows-sys", 339 | ] 340 | 341 | [[package]] 342 | name = "windows-sys" 343 | version = "0.59.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 346 | dependencies = [ 347 | "windows-targets", 348 | ] 349 | 350 | [[package]] 351 | name = "windows-targets" 352 | version = "0.52.6" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 355 | dependencies = [ 356 | "windows_aarch64_gnullvm", 357 | "windows_aarch64_msvc", 358 | "windows_i686_gnu", 359 | "windows_i686_gnullvm", 360 | "windows_i686_msvc", 361 | "windows_x86_64_gnu", 362 | "windows_x86_64_gnullvm", 363 | "windows_x86_64_msvc", 364 | ] 365 | 366 | [[package]] 367 | name = "windows_aarch64_gnullvm" 368 | version = "0.52.6" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 371 | 372 | [[package]] 373 | name = "windows_aarch64_msvc" 374 | version = "0.52.6" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 377 | 378 | [[package]] 379 | name = "windows_i686_gnu" 380 | version = "0.52.6" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 383 | 384 | [[package]] 385 | name = "windows_i686_gnullvm" 386 | version = "0.52.6" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 389 | 390 | [[package]] 391 | name = "windows_i686_msvc" 392 | version = "0.52.6" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 395 | 396 | [[package]] 397 | name = "windows_x86_64_gnu" 398 | version = "0.52.6" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 401 | 402 | [[package]] 403 | name = "windows_x86_64_gnullvm" 404 | version = "0.52.6" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 407 | 408 | [[package]] 409 | name = "windows_x86_64_msvc" 410 | version = "0.52.6" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 413 | 414 | [[package]] 415 | name = "winnow" 416 | version = "0.7.11" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" 419 | dependencies = [ 420 | "memchr", 421 | ] 422 | 423 | [[package]] 424 | name = "zerocopy" 425 | version = "0.8.25" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" 428 | dependencies = [ 429 | "zerocopy-derive", 430 | ] 431 | 432 | [[package]] 433 | name = "zerocopy-derive" 434 | version = "0.8.25" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" 437 | dependencies = [ 438 | "proc-macro2", 439 | "quote", 440 | "syn", 441 | ] 442 | -------------------------------------------------------------------------------- /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_call_site_warning, 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 | } else if !cfg!(feature = "allow-trait-autoimpl-with-sized-fn-bound") { 202 | // TODO(rust proc_macro_lint): this should be a configurable lint 203 | emit_call_site_warning!( 204 | "autoimpl on trait that has a method with Self: Sized bound"; 205 | note = item.span() => "method impl uses default implementation, not deref"; 206 | ); 207 | } 208 | 209 | continue; 210 | } 211 | 212 | for (i, arg) in item.sig.inputs.iter_mut().enumerate() { 213 | if let FnArg::Typed(ref mut ty) = arg { 214 | if let Pat::Ident(pat) = &mut *ty.pat { 215 | // We can keep the ident but must not use `ref` / `mut` modifiers 216 | pat.by_ref = None; 217 | pat.mutability = None; 218 | assert_eq!(pat.subpat, None); 219 | } else { 220 | // Substitute a fresh ident 221 | let name = format!("arg{i}"); 222 | let ident = Ident::new(&name, Span::call_site()); 223 | ty.pat = Box::new(Pat::Ident(syn::PatIdent { 224 | attrs: vec![], 225 | by_ref: None, 226 | mutability: None, 227 | ident, 228 | subpat: None, 229 | })); 230 | } 231 | } 232 | } 233 | item.sig.to_tokens(tokens); 234 | 235 | bound = bound.max(match item.sig.inputs.first() { 236 | Some(FnArg::Receiver(rec)) => { 237 | if rec.reference.is_some() { 238 | Bound::Deref(rec.mutability.is_some()) 239 | } else { 240 | emit_call_site_error!( 241 | "cannot autoimpl trait with Deref"; 242 | note = rec.span() => "deref cannot yield `self` by value"; 243 | ); 244 | Bound::ErrorEmitted 245 | } 246 | } 247 | Some(FnArg::Typed(ref pat)) => match &*pat.ty { 248 | Type::Reference(rf) if rf.elem == parse_quote! { Self } => { 249 | Bound::Deref(rf.mutability.is_some()) 250 | } 251 | _ => Bound::None, 252 | }, 253 | _ => Bound::None, 254 | }); 255 | 256 | let ident = &item.sig.ident; 257 | let params = item.sig.inputs.iter().map(|arg| { 258 | let mut toks = TokenStream::new(); 259 | match arg { 260 | FnArg::Receiver(arg) => { 261 | for attr in &arg.attrs { 262 | if propegate_attr_to_impl(&attr) { 263 | attr.to_tokens(&mut toks); 264 | } 265 | } 266 | arg.self_token.to_tokens(&mut toks); 267 | } 268 | FnArg::Typed(arg) => { 269 | for attr in &arg.attrs { 270 | if propegate_attr_to_impl(&attr) { 271 | attr.to_tokens(&mut toks); 272 | }; 273 | } 274 | 275 | arg.pat.to_tokens(&mut toks); 276 | } 277 | }; 278 | toks 279 | }); 280 | tokens.append_all(quote! { { 281 | #definitive :: #ident ( #(#params),* ) 282 | } }); 283 | } 284 | TraitItem::Type(item) => { 285 | for attr in item.attrs.iter() { 286 | if *attr.path() == parse_quote! { cfg } { 287 | attr.to_tokens(tokens); 288 | } 289 | } 290 | 291 | if has_bound_on_self(&item.generics) { 292 | emit_call_site_error!( 293 | "cannot autoimpl trait with Deref"; 294 | note = item.span() => "type has a bound on Self"; 295 | ); 296 | } 297 | 298 | item.type_token.to_tokens(tokens); 299 | item.ident.to_tokens(tokens); 300 | 301 | let (_, ty_generics, where_clause) = item.generics.split_for_impl(); 302 | ty_generics.to_tokens(tokens); 303 | 304 | Eq::default().to_tokens(tokens); 305 | definitive.to_tokens(tokens); 306 | PathSep::default().to_tokens(tokens); 307 | item.ident.to_tokens(tokens); 308 | ty_generics.to_tokens(tokens); 309 | 310 | where_clause.to_tokens(tokens); 311 | item.semi_token.to_tokens(tokens); 312 | } 313 | TraitItem::Macro(item) => { 314 | emit_error!(item, "unsupported: macro item in trait"); 315 | } 316 | TraitItem::Verbatim(item) => { 317 | emit_error!(item, "unsupported: verbatim item in trait"); 318 | } 319 | 320 | /* Testing of exhaustive matching is disabled: syn 1.0.90 breaks it. 321 | #[cfg(test)] 322 | TraitItem::__TestExhaustive(_) => unimplemented!(), 323 | #[cfg(not(test))] 324 | */ 325 | _ => (), 326 | } 327 | } 328 | 329 | let mut toks = TokenStream::new(); 330 | match bound { 331 | Bound::None => (), 332 | Bound::Deref(is_mut) => { 333 | // Emit a bound to improve error messages (see issue 27) 334 | let bound = match is_mut { 335 | false => quote! { ::core::ops::Deref }, 336 | true => quote! { ::core::ops::DerefMut }, 337 | }; 338 | 339 | let target_impls = self.targets.iter().map(|target| { 340 | quote! { 341 | impl #impl_generics TargetMustImplDeref #ty_generics for #target 342 | #where_clause {} 343 | } 344 | }); 345 | 346 | toks.append_all(quote! { 347 | #[automatically_derived] 348 | const _: () = { 349 | trait TargetMustImplDeref #impl_generics: #bound 350 | #where_clause {} 351 | 352 | #(#target_impls)* 353 | }; 354 | }); 355 | } 356 | Bound::ErrorEmitted => return toks, 357 | } 358 | 359 | for target in self.targets { 360 | toks.append_all(quote! { 361 | #[automatically_derived] 362 | impl #impl_generics #trait_ty for #target #where_clause { 363 | #impl_items 364 | } 365 | }); 366 | } 367 | toks 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /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 an alternative to 16 | //! `#[derive(Default)]` supporting field initializers. 17 | //! 18 | //! [`#[impl_self]`](macro@impl_self) provides `impl Self` syntax, avoiding the 19 | //! need to repeat generics when writing impls on a local type definition. 20 | //! This supercedes [`impl_scope!`] (except regarding [`macro@impl_default`]). 21 | //! 22 | //! [`impl_anon!`] is a function-like macro used to define and instantiate a 23 | //! unique (single-use) type. It supports everything supported by [`impl_scope!`] 24 | //! plus field initializers and (limited) automatic typing of fields. 25 | //! 26 | //! User-extensions to both [`#[autoimpl]`](macro@autoimpl) and [`macro@impl_self`] 27 | //! are possible with a custom proc-macro crate depending on 28 | //! [impl-tools-lib](https://crates.io/crates/impl-tools-lib). 29 | 30 | #[cfg(doctest)] 31 | doc_comment::doctest!("../README.md"); 32 | 33 | extern crate proc_macro; 34 | 35 | use lib::{anon, scope}; 36 | use proc_macro::TokenStream; 37 | use proc_macro_error2::{emit_call_site_error, proc_macro_error}; 38 | use syn::parse_macro_input; 39 | 40 | use impl_tools_lib::{self as lib, autoimpl}; 41 | 42 | /// Impl [`Default`] with given field or type initializers 43 | /// 44 | /// This macro may be used in one of two ways. 45 | /// 46 | /// NOTE: this macro is already partially obsolete since Rust 1.62 added support 47 | /// for [default enum variants](https://blog.rust-lang.org/2022/06/30/Rust-1.62.0/#default-enum-variants). 48 | /// Once [RFC 3681](https://github.com/rust-lang/rfcs/pull/3681) (default field values) 49 | /// is stable in this crate's MSRV, this macro will be deprecated. 50 | /// 51 | /// ### Type-level initializer 52 | /// 53 | /// ``` 54 | /// # use impl_tools::impl_default; 55 | /// /// A simple enum; default value is Blue 56 | /// #[impl_default(Colour::Blue)] 57 | /// enum Colour { 58 | /// Red, 59 | /// Green, 60 | /// Blue, 61 | /// } 62 | /// 63 | /// fn main() { 64 | /// assert!(matches!(Colour::default(), Colour::Blue)); 65 | /// } 66 | /// ``` 67 | /// 68 | /// A where clause is optional: `#[impl_default(EXPR where BOUNDS)]`. 69 | /// 70 | /// ### Field-level initializer 71 | /// 72 | /// This variant only supports structs. Fields specified as `name: type = expr` 73 | /// will be initialized with `expr`, while other fields will be initialized with 74 | /// `Default::default()`. 75 | /// 76 | /// ``` 77 | /// # use impl_tools::{impl_default, impl_scope}; 78 | /// impl_scope! { 79 | /// #[impl_default] 80 | /// struct Person { 81 | /// name: String = "Jane Doe".to_string(), 82 | /// age: u32 = 72, 83 | /// occupation: String, 84 | /// } 85 | /// } 86 | /// 87 | /// fn main() { 88 | /// let person = Person::default(); 89 | /// assert_eq!(person.name, "Jane Doe"); 90 | /// assert_eq!(person.age, 72); 91 | /// assert_eq!(person.occupation, ""); 92 | /// } 93 | /// ``` 94 | /// 95 | /// A where clause is optional: `#[impl_default(where BOUNDS)]`. 96 | #[proc_macro_attribute] 97 | #[proc_macro_error] 98 | pub fn impl_default(args: TokenStream, item: TokenStream) -> TokenStream { 99 | let mut toks = item.clone(); 100 | match syn::parse::(args) { 101 | Ok(attr) => toks.extend(TokenStream::from(attr.expand(item.into()))), 102 | Err(err) => { 103 | emit_call_site_error!(err); 104 | // Since this form of invocation only adds implementations, we can 105 | // safely output the original item, thus reducing secondary errors. 106 | } 107 | } 108 | toks 109 | } 110 | 111 | /// An alternative to the standard [`macro@derive`] macro 112 | /// 113 | /// This macro may be used: 114 | /// 115 | /// - [On a type definition](#on-type-definitions), to implement a specified trait 116 | /// - [On a trait definition](#on-trait-definitions), to implement the trait for specified types 117 | /// supporting [`Deref`] 118 | /// 119 | /// # On type definitions 120 | /// 121 | /// `#[autoimpl]` on type definitions functions similarly to [`#[derive]`](macro@derive). The differences are as follows. 122 | /// 123 | /// 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: 124 | /// ``` 125 | /// # use impl_tools::autoimpl; 126 | /// #[autoimpl(Clone, Debug where T: trait)] 127 | /// struct Wrapper(pub T); 128 | /// ``` 129 | /// 130 | /// ### `ignore` 131 | /// 132 | /// Traits like [`Debug`] may be implemented while `ignore`-ing some fields, for example: 133 | /// ``` 134 | /// # use impl_tools::autoimpl; 135 | /// #[autoimpl(Debug ignore self.f)] 136 | /// struct PairWithFn { 137 | /// x: f32, 138 | /// y: f32, 139 | /// f: fn(&T), 140 | /// } 141 | /// ``` 142 | /// 143 | /// ### `using` 144 | /// 145 | /// Traits like [`Deref`] may be implemented by `using` a named field, for example: 146 | /// ``` 147 | /// # use impl_tools::autoimpl; 148 | /// #[autoimpl(Deref, DerefMut using self.1)] 149 | /// struct AnnotatedWrapper(String, T); 150 | /// ``` 151 | /// In the above example, [`Deref::Target`] will be implemented as `T` (the type 152 | /// of the field `self.1`). The `Target` type may instead be specified explicitly: 153 | /// ``` 154 | /// # use impl_tools::autoimpl; 155 | /// #[autoimpl(Deref using self.0)] 156 | /// struct MyBoxingWrapper(Box); 157 | /// ``` 158 | /// 159 | /// ## Supported traits 160 | /// 161 | /// | Path | *ignore* | *using* | *notes* | 162 | /// |----- |--- |--- |--- | 163 | /// | [`::core::borrow::Borrow`] | - | borrow target | `T` is type of target field | 164 | /// | [`::core::borrow::BorrowMut`] | - | borrow target | `T` is type of target field | 165 | /// | [`::core::clone::Clone`] | yes | - | ignored fields use `Default::default()` | 166 | /// | [`::core::cmp::Eq`] | * | - | *allowed with `PartialEq` | 167 | /// | [`::core::cmp::Ord`] | yes | - | | 168 | /// | [`::core::cmp::PartialEq`] | yes | - | | 169 | /// | [`::core::cmp::PartialOrd`] | yes | - | | 170 | /// | [`::core::convert::AsRef`] | - | ref target | `T` is type of target field | 171 | /// | [`::core::convert::AsMut`] | - | ref target | `T` is type of target field | 172 | /// | [`::core::default::Default`] | - | - | [`macro@impl_default`] is a more flexible alternative | 173 | /// | [`::core::fmt::Debug`] | yes | - | | 174 | /// | [`::core::hash::Hash`] | yes | - | | 175 | /// | [`::core::marker::Copy`] | * | - | *allowed with `Clone` | 176 | /// | [`::core::ops::Deref`] | - | deref target | See [`Deref::Target` type](#dereftarget-type) below | 177 | /// | [`::core::ops::DerefMut`] | - | deref target | | 178 | /// 179 | /// Traits are matched using the path, as follows: 180 | /// 181 | /// - Only the last component, e.g. `#[autoimpl(Clone)]` 182 | /// - The full path with leading `::`, e.g. `#[autoimpl(::core::clone::Clone)]` 183 | /// - The full path without leading `::`, e.g. `#[autoimpl(core::clone::Clone)]` 184 | /// - The full path with/without leading `::`, using `std` instead of `core` or `alloc`, 185 | /// e.g. `#[autoimpl(std::clone::Clone)]` 186 | /// 187 | /// ## Parameter syntax 188 | /// 189 | /// > _ParamsMulti_ :\ 190 | /// >    ( _Trait_ ),+ _Using_? _Ignores_? _WhereClause_? 191 | /// > 192 | /// > _Using_ :\ 193 | /// >    `using` `self` `.` _Member_ 194 | /// > 195 | /// > _Ignores_ :\ 196 | /// >    `ignore` ( `self` `.` _Member_ ),+ 197 | /// > 198 | /// > _WhereClause_ :\ 199 | /// >    `where` ( _WherePredicate_ ),* 200 | /// 201 | /// **Targets:** each *Trait* listed is implemented for the annotated type. 202 | /// 203 | /// 204 | /// # On trait definitions 205 | /// 206 | /// `#[autoimpl]` on trait definitions generates an implementation of that trait 207 | /// for the given targets. This functions using an implementation of [`Deref`] 208 | /// (and, where required, [`DerefMut`]) to lower the target type to some other 209 | /// type supporting the trait. We call this latter type the **definitive type**. 210 | /// 211 | /// It is required that the target type(s) implemented are generic over some 212 | /// type parameter(s). These generic parameters are introduced using `for<..>`. 213 | /// It is further required that at least one generic parameter has a bound on 214 | /// `trait`; the first such parameter is inferred to be the *definitive type*. 215 | /// 216 | /// For example, the following usage implements `MyTrait` for targets `&T`, 217 | /// `&mut T` and `Box` using definitive type `T`: 218 | /// ``` 219 | /// # use impl_tools::autoimpl; 220 | /// #[autoimpl(for &T, &mut T, Box)] 221 | /// trait MyTrait { 222 | /// fn f(&self) -> String; 223 | /// } 224 | /// ``` 225 | /// The expansion for target `Box` looks like: 226 | /// ``` 227 | /// # trait MyTrait { 228 | /// # fn f(&self) -> String; 229 | /// # } 230 | /// #[automatically_derived] 231 | /// impl MyTrait for Box { 232 | /// fn f(&self) -> String { 233 | /// ::f(self) 234 | /// } 235 | /// } 236 | /// ``` 237 | /// 238 | /// ## Generics 239 | /// 240 | /// Traits using generics and trait items using generics are, for the most part, 241 | /// supported. 242 | /// 243 | /// Items with a where clause with a type bound on `Self` cannot be implemented 244 | /// via [`Deref`] since the item is not guaranteed to exist on the definitive 245 | /// type. Such items with a default implementation may be implemented using this 246 | /// that default implementation, though this results in a warning by default 247 | /// (requires feature "nightly-diagnostics"). 248 | /// In other cases an error is reported. 249 | /// 250 | /// An example: 251 | /// ``` 252 | /// # use impl_tools::autoimpl; 253 | /// # use std::fmt::Debug; 254 | /// #[autoimpl(for<'a, T> &'a T, &'a mut T, Box where T: trait + ?Sized)] 255 | /// trait G 256 | /// where 257 | /// V: Debug, 258 | /// { 259 | /// fn g(&self) -> V; 260 | /// 261 | /// fn s(&self, f: impl Fn(V) -> X) -> X 262 | /// where 263 | /// Self: Sized, 264 | /// { 265 | /// f(self.g()) 266 | /// } 267 | /// } 268 | /// ``` 269 | /// 270 | /// ## Parameter syntax 271 | /// 272 | /// > _ParamsTrait_ :\ 273 | /// >    `for` _Generics_ ( _Type_ ),+ _WhereClause_? 274 | /// 275 | /// [`Deref`]: std::ops::Deref 276 | /// [`Deref::Target`]: std::ops::Deref::Target 277 | /// [`DerefMut`]: std::ops::DerefMut 278 | #[proc_macro_attribute] 279 | #[proc_macro_error] 280 | pub fn autoimpl(attr: TokenStream, item: TokenStream) -> TokenStream { 281 | let mut toks = item.clone(); 282 | match syn::parse::(attr) { 283 | Ok(autoimpl::Attr::ForDeref(ai)) => toks.extend(TokenStream::from(ai.expand(item.into()))), 284 | Ok(autoimpl::Attr::ImplTraits(ai)) => { 285 | // We could use lazy_static to construct a HashMap for fast lookups, 286 | // but given the small number of impls a "linear map" is fine. 287 | let find_impl = |path: &syn::Path| { 288 | autoimpl::STD_IMPLS 289 | .iter() 290 | .cloned() 291 | .find(|impl_| impl_.path().matches_ident_or_path(path)) 292 | }; 293 | toks.extend(TokenStream::from(ai.expand(item.into(), find_impl))) 294 | } 295 | Err(err) => { 296 | emit_call_site_error!(err); 297 | // Since autoimpl only adds implementations, we can safely output 298 | // the original item, thus reducing secondary errors. 299 | } 300 | } 301 | toks 302 | } 303 | 304 | /// Implement a type with `impl Self` syntax 305 | /// 306 | /// This macro facilitates definition of a type (struct, enum or union) plus 307 | /// implementations via `impl Self { .. }` syntax: `Self` is expanded to the 308 | /// type's name, including generics and bounds (as defined on the type). 309 | /// 310 | /// Caveat: `rustfmt` can not yet format contents (see 311 | /// [rustfmt#5254](https://github.com/rust-lang/rustfmt/issues/5254), 312 | /// [rustfmt#5538](https://github.com/rust-lang/rustfmt/pull/5538)). 313 | /// 314 | /// Note: this macro is largely redundant with the [`macro@impl_self`] macro, 315 | /// the one exception being [`macro@impl_default`] support. This macro will be 316 | /// deprecated simultaneously with [`macro@impl_default`]. 317 | /// 318 | /// ## Special attribute macros 319 | /// 320 | /// Additionally, `impl_scope!` supports special attribute macros evaluated 321 | /// within its scope: 322 | /// 323 | /// - [`#[impl_default]`](macro@impl_default): implement [`Default`] using 324 | /// field initializers (which are not legal syntax outside of `impl_scope!`) 325 | /// 326 | /// Note: matching these macros within `impl_scope!` does not use path 327 | /// resolution. Using `#[impl_tools::impl_default]` would resolve the variant 328 | /// of this macro which *doesn't support* field initializers. 329 | /// 330 | /// ## Syntax 331 | /// 332 | /// > _ImplScope_ :\ 333 | /// >    `impl_scope!` `{` _ScopeItem_ _ItemImpl_ * `}` 334 | /// > 335 | /// > _ScopeItem_ :\ 336 | /// >    _ItemEnum_ | _ItemStruct_ | _ItemType_ | _ItemUnion_ 337 | /// 338 | /// That is, one type definition followed by a set of implementations. 339 | /// Impls must take one of two forms: 340 | /// 341 | /// - `impl Self { ... }` — generic parameters and bounds of the type are used 342 | /// - `impl MyType { ... }` where `MyType` matches the name of the defined type 343 | /// 344 | /// Generic parameters from the type are included implicitly with the first form. 345 | /// Additional generic parameters and where clauses are supported (parameters 346 | /// and bounds are merged). 347 | /// 348 | /// ## Example 349 | /// 350 | /// ``` 351 | /// impl_tools::impl_scope! { 352 | /// struct Pair(T, T); 353 | /// 354 | /// impl Self { 355 | /// pub fn new(a: T, b: T) -> Self { 356 | /// Pair(a, b) 357 | /// } 358 | /// } 359 | /// 360 | /// impl Self where T: Clone { 361 | /// pub fn splat(a: T) -> Self { 362 | /// let b = a.clone(); 363 | /// Pair(a, b) 364 | /// } 365 | /// } 366 | /// } 367 | /// ``` 368 | #[proc_macro_error] 369 | #[proc_macro] 370 | pub fn impl_scope(input: TokenStream) -> TokenStream { 371 | let mut scope = parse_macro_input!(input as scope::Scope); 372 | scope.apply_attrs(scope::find_impl_default); 373 | scope.expand().into() 374 | } 375 | 376 | /// Construct an anonymous struct 377 | /// 378 | /// Rust doesn't currently support [`impl Trait { ... }` expressions](https://github.com/canndrew/rfcs/blob/impl-trait-expressions/text/0000-impl-trait-expressions.md) 379 | /// or implicit typing of struct fields. This macro is a **hack** allowing that. 380 | /// 381 | /// Example: 382 | /// ``` 383 | /// use std::fmt; 384 | /// fn main() { 385 | /// let world = "world"; 386 | /// let says_hello_world = impl_tools::impl_anon! { 387 | /// struct(&'static str = world); 388 | /// impl fmt::Display for Self { 389 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 390 | /// write!(f, "hello {}", self.0) 391 | /// } 392 | /// } 393 | /// }; 394 | /// assert_eq!(format!("{}", says_hello_world), "hello world"); 395 | /// } 396 | /// ``` 397 | /// 398 | /// That is, this macro creates an anonymous struct type (must be a struct), 399 | /// which may have trait implementations, then creates an instance of that 400 | /// struct. 401 | /// 402 | /// Struct fields may have a fixed type or may be generic. Syntax is as follows: 403 | /// 404 | /// - **regular struct:** `ident: ty = value` 405 | /// - **regular struct:** `ident: ty` (uses `Default` to construct value) 406 | /// - **regular struct:** `ident = value` (type is generic without bounds) 407 | /// - **tuple struct:** `ty = value` 408 | /// - **tuple struct:** `ty` (uses `Default` to construct value) 409 | /// 410 | /// The field name, `ident`, may be `_` (anonymous field). 411 | /// 412 | /// The field type, `ty`, may be or may contain inferred types (`_`) and/or 413 | /// `impl Trait` type expressions. These are substituted with generics on the 414 | /// type. 415 | /// 416 | /// Refer to [examples](https://github.com/search?q=impl_anon+repo%3Akas-gui%2Fkas+path%3Aexamples&type=Code) for usage. 417 | #[proc_macro_error] 418 | #[proc_macro] 419 | pub fn impl_anon(input: TokenStream) -> TokenStream { 420 | let mut scope = parse_macro_input!(input as anon::Anon).into_scope(); 421 | scope.apply_attrs(scope::find_impl_default); 422 | scope.expand().into() 423 | } 424 | 425 | /// Implement a type with `impl Self` syntax 426 | /// 427 | /// This attribute macro supports a type (struct, enum, type alias or union) 428 | /// definition plus associated `impl` items within a `mod`. 429 | /// 430 | /// Macro expansion discards the `mod` entirely, placing all contents into the 431 | /// outer scope. This simplifies privacy rules in many use-cases, and highlights 432 | /// that the usage of `mod` is purely a hack to make the macro input valid Rust 433 | /// syntax (and thus compatible with `rustfmt`). 434 | /// 435 | /// ## Syntax 436 | /// 437 | /// > _ImplSelf_ :\ 438 | /// >    `#[impl_self]` `mod` _Name_ `{` _ScopeItem_ _ItemImpl_ * `}` 439 | /// > 440 | /// > _ScopeItem_ :\ 441 | /// >    _ItemEnum_ | _ItemStruct_ | _ItemType_ | _ItemUnion_ 442 | /// 443 | /// Here, _ItemEnum_, _ItemStruct_, _ItemType_ and _ItemUnion_ are `enum`, 444 | /// `struct`, `type` alias and `union` definitions respectively. Whichever of 445 | /// these is used, it must match the module name _Name_. 446 | /// 447 | /// _ItemImpl_ is an `impl` item. It may use the standard implementation syntax 448 | /// (e.g. `impl Debug for MyType { .. }`) or `impl Self` syntax (see below). 449 | /// 450 | /// The `mod` may not contain any other items, except `doc` items (documentation 451 | /// on the module itself is ignored in favour of documentation on the defined 452 | /// type) and attributes (which apply as usual). 453 | /// 454 | /// ### `impl Self` syntax 455 | /// 456 | /// `impl Self` "syntax" is syntactically-valid (but not semantically-valid) 457 | /// Rust syntax for writing inherent and trait `impl` blocks: 458 | /// 459 | /// - `impl Self { ... }` — an inherent `impl` item on the defined type 460 | /// - `impl Debug for Self { ... }` — a trait `impl` item on the defined type 461 | /// 462 | /// Generic parameters and bounds are copied from the type definition. 463 | /// Additional generic parameters may be specified; these extend the list of 464 | /// generic parameters on the type itself, and thus must have distinct names. 465 | /// Additional bounds (where clauses) may be specified; these extend the list of 466 | /// bounds on the type itself. 467 | /// 468 | /// ## Example 469 | /// 470 | /// ``` 471 | /// #[impl_tools::impl_self] 472 | /// mod Pair { 473 | /// /// A pair of values of type `T` 474 | /// pub struct Pair(T, T); 475 | /// 476 | /// impl Self { 477 | /// pub fn new(a: T, b: T) -> Self { 478 | /// Pair(a, b) 479 | /// } 480 | /// } 481 | /// 482 | /// impl Self where T: Clone { 483 | /// pub fn splat(a: T) -> Self { 484 | /// let b = a.clone(); 485 | /// Pair(a, b) 486 | /// } 487 | /// } 488 | /// } 489 | /// ``` 490 | #[proc_macro_attribute] 491 | #[proc_macro_error] 492 | pub fn impl_self(attr: TokenStream, input: TokenStream) -> TokenStream { 493 | let _ = parse_macro_input!(attr as scope::ScopeModAttrs); 494 | let scope = parse_macro_input!(input as scope::ScopeMod); 495 | scope.contents.expand().into() 496 | } 497 | -------------------------------------------------------------------------------- /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(_) => syn::Error::new( 306 | Span::call_site(), 307 | "#[autoimpl(Trait)] can only be used on enum or struct items", 308 | ) 309 | .into_compile_error(), 310 | Err(err) => err.into_compile_error(), 311 | } 312 | } 313 | 314 | fn expand_enum( 315 | self, 316 | item: ItemEnum, 317 | find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>, 318 | ) -> Toks { 319 | let ImplTraits { 320 | mut targets, 321 | mut args, 322 | } = self; 323 | 324 | if !args.ignores.is_empty() { 325 | let ignores = args.ignores.iter(); 326 | let list = quote! { #(#ignores)* }; 327 | emit_error!(list, "enum expansion does not currently support `ignore`",); 328 | return Toks::new(); 329 | } 330 | if let Some(mem) = args.using { 331 | emit_error!(mem, "enum expansion does not currently support `using`",); 332 | return Toks::new(); 333 | } 334 | 335 | let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len()); 336 | for mut target in targets.drain(..) { 337 | let target_span = target.span(); 338 | let path_args = target 339 | .segments 340 | .last_mut() 341 | .map(|seg| std::mem::take(&mut seg.arguments)) 342 | .unwrap_or(PathArguments::None); 343 | let target_impl = match find_impl(&target) { 344 | Some(impl_) => impl_, 345 | None => { 346 | emit_error!(target, "unsupported trait"); 347 | return Toks::new(); 348 | } 349 | }; 350 | 351 | if !(path_args.is_empty() || target_impl.support_path_arguments()) { 352 | emit_error!( 353 | target_span, 354 | "target {} does not support path arguments", 355 | target_impl.path() 356 | ); 357 | } 358 | 359 | impl_targets.push((target.span(), target_impl, path_args)); 360 | } 361 | 362 | let mut toks = Toks::new(); 363 | 364 | for (span, target, path_args) in impl_targets.drain(..) { 365 | let path_args_span = path_args.span(); 366 | args.path_arguments = path_args; 367 | match target.enum_impl(&item, &args) { 368 | Ok(items) => toks.append_all(items), 369 | Err(error) => error.emit(span, path_args_span), 370 | } 371 | } 372 | toks 373 | } 374 | 375 | fn expand_struct( 376 | self, 377 | item: ItemStruct, 378 | find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>, 379 | ) -> Toks { 380 | let ImplTraits { 381 | mut targets, 382 | mut args, 383 | } = self; 384 | 385 | let mut not_supporting_ignore = vec![]; 386 | let mut not_supporting_using = vec![]; 387 | 388 | let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len()); 389 | for mut target in targets.drain(..) { 390 | let target_span = target.span(); 391 | let path_args = target 392 | .segments 393 | .last_mut() 394 | .map(|seg| std::mem::take(&mut seg.arguments)) 395 | .unwrap_or(PathArguments::None); 396 | let target_impl = match find_impl(&target) { 397 | Some(impl_) => impl_, 398 | None => { 399 | emit_error!(target, "unsupported trait"); 400 | return Toks::new(); 401 | } 402 | }; 403 | 404 | if !target_impl.support_ignore() { 405 | let except_with = target_impl.allow_ignore_with(); 406 | not_supporting_ignore.push((target.clone(), except_with)); 407 | } 408 | if !target_impl.support_using() { 409 | not_supporting_using.push(target.clone()); 410 | } 411 | if !(path_args.is_empty() || target_impl.support_path_arguments()) { 412 | emit_error!( 413 | target_span, 414 | "target {} does not support path arguments", 415 | target_impl.path() 416 | ); 417 | } 418 | 419 | impl_targets.push((target.span(), target_impl, path_args)); 420 | } 421 | 422 | if !args.ignores.is_empty() { 423 | for (target, except_with) in not_supporting_ignore.into_iter() { 424 | if let Some(path) = except_with { 425 | if impl_targets 426 | .iter() 427 | .any(|(_, target_impl, _)| path == target_impl.path()) 428 | { 429 | continue; 430 | } 431 | } 432 | emit_error!(target, "target does not support `ignore`",); 433 | } 434 | } 435 | if args.using.is_some() { 436 | for target in not_supporting_using.into_iter() { 437 | emit_error!(target, "target does not support `using`",); 438 | } 439 | } 440 | 441 | fn check_is_field(mem: &Member, fields: &Fields) { 442 | match (fields, mem) { 443 | (Fields::Named(fields), Member::Named(ref ident)) => { 444 | if fields 445 | .named 446 | .iter() 447 | .any(|field| field.ident.as_ref() == Some(ident)) 448 | { 449 | return; 450 | } 451 | } 452 | (Fields::Unnamed(fields), Member::Unnamed(index)) => { 453 | if (index.index as usize) < fields.unnamed.len() { 454 | return; 455 | } 456 | } 457 | _ => (), 458 | } 459 | emit_error!(mem, "not a struct field"); 460 | } 461 | 462 | let mut toks = Toks::new(); 463 | for mem in &args.ignores { 464 | check_is_field(mem, &item.fields); 465 | } 466 | if let Some(mem) = args.using_member() { 467 | check_is_field(mem, &item.fields); 468 | } 469 | 470 | for (span, target, path_args) in impl_targets.drain(..) { 471 | let path_args_span = path_args.span(); 472 | args.path_arguments = path_args; 473 | match target.struct_impl(&item, &args) { 474 | Ok(items) => toks.append_all(items), 475 | Err(error) => error.emit(span, path_args_span), 476 | } 477 | } 478 | toks 479 | } 480 | } 481 | 482 | /// Arguments passed to [`ImplTrait`] implementation methods 483 | pub struct ImplArgs { 484 | /// Path arguments to trait 485 | /// 486 | /// Example: if the target is `Deref`, this is ``. 487 | /// This is always empty unless [`ImplTrait::support_path_arguments`] returns true. 488 | pub path_arguments: PathArguments, 489 | /// Fields ignored in attribute 490 | pub ignores: Vec, 491 | /// Field specified to 'use' in attribute 492 | pub using: Option, 493 | /// Where clause added to attribute 494 | pub clause: Option, 495 | } 496 | 497 | impl ImplArgs { 498 | /// If true, this field is ignored 499 | pub fn ignore(&self, member: &Member) -> bool { 500 | self.ignores.contains(member) 501 | } 502 | 503 | /// If true, this named field is ignored 504 | pub fn ignore_named(&self, ident: &Ident) -> bool { 505 | self.ignores.iter().any(|ig| match ig { 506 | Member::Named(m) => m == ident, 507 | _ => false, 508 | }) 509 | } 510 | 511 | /// If true, this unnamed field is ignored 512 | pub fn ignore_unnamed(&self, index: &Index) -> bool { 513 | self.ignores.iter().any(|ig| match ig { 514 | Member::Unnamed(m) => m == index, 515 | _ => false, 516 | }) 517 | } 518 | 519 | /// Field to "use", if any 520 | pub fn using_member(&self) -> Option<&Member> { 521 | self.using.as_ref() 522 | } 523 | 524 | /// Find field to "use", if any 525 | pub fn using_field<'b>(&self, fields: &'b Fields) -> Option<&'b Field> { 526 | match fields { 527 | Fields::Named(fields) => fields.named.iter().find(|field| match self.using { 528 | Some(Member::Named(ref ident)) => ident == field.ident.as_ref().unwrap(), 529 | _ => false, 530 | }), 531 | Fields::Unnamed(fields) => { 532 | fields 533 | .unnamed 534 | .iter() 535 | .enumerate() 536 | .find_map(|(i, field)| match self.using { 537 | Some(Member::Unnamed(ref index)) => { 538 | (*index == Index::from(i)).then(|| field) 539 | } 540 | _ => None, 541 | }) 542 | } 543 | Fields::Unit => None, 544 | } 545 | } 546 | 547 | /// Call the given closure over all non-ignored fields 548 | pub fn for_fields<'f>(&self, fields: &'f Fields, f: impl FnMut(Member, &'f Field)) { 549 | self.for_fields_iter(fields.iter().enumerate(), f); 550 | } 551 | 552 | /// Call the given closure over all non-ignored fields 553 | pub fn for_fields_iter<'f>( 554 | &self, 555 | fields: impl Iterator, 556 | mut f: impl FnMut(Member, &'f Field), 557 | ) { 558 | for (i, field) in fields { 559 | let member = match field.ident.clone() { 560 | Some(ident) => Member::Named(ident), 561 | None => Member::Unnamed(Index::from(i)), 562 | }; 563 | if !self.ignore(&member) { 564 | f(member, field); 565 | } 566 | } 567 | } 568 | } 569 | -------------------------------------------------------------------------------- /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 for `#[impl_scope]` 23 | pub struct ScopeModAttrs; 24 | 25 | /// Attribute rule for [`Scope`] 26 | /// 27 | /// Rules are matched via a path, e.g. `&["foo"]` matches `foo` and 28 | /// `&["", "foo", "bar"]` matches `::foo::bar`. 29 | /// 30 | /// Such rules are used to expand attributes within an `impl_scope!`. 31 | pub trait ScopeAttr { 32 | /// Attribute path 33 | /// 34 | /// Rules are matched via a path, e.g. `&["foo"]` matches `foo` and 35 | /// `&["", "foo", "bar"]` matches `::foo::bar`. 36 | /// 37 | /// Note that we cannot use standard path resolution, so we match only a 38 | /// single path, as defined. 39 | fn path(&self) -> SimplePath; 40 | 41 | /// Whether repeated application is valid 42 | /// 43 | /// If this is false (the default), then an error will be omitted on 44 | /// repeated usage of the attribute. This mostly serves to emit better error 45 | /// messages in cases where the first application modifies the input. 46 | fn support_repetition(&self) -> bool { 47 | false 48 | } 49 | 50 | /// Function type of [`ScopeAttr`] rule 51 | /// 52 | /// Input arguments: 53 | /// 54 | /// - `attr`: the invoking attribute. It is suggested to parse arguments 55 | /// using [`Attribute::parse_args`] or [`Attribute::parse_args_with`]. 56 | /// - `scope`: mutable reference to the implementation scope. Usually 57 | /// an attribute rule function will read data from the scope and append its 58 | /// output to [`Scope::generated`]. 59 | fn apply(&self, attr: Attribute, scope: &mut Scope) -> Result<()>; 60 | } 61 | 62 | /// Content of items supported by [`Scope`] that are not common to all variants 63 | #[derive(Debug)] 64 | pub enum ScopeItem { 65 | /// A [`syn::ItemEnum`], minus common parts 66 | Enum { 67 | /// `enum` 68 | token: Token![enum], 69 | /// `{ ... }` 70 | brace: Brace, 71 | /// Variants of enum 72 | variants: Punctuated, 73 | }, 74 | /// A [`syn::ItemStruct`], minus common parts 75 | /// 76 | /// Uses custom [`Fields`], supporting field initializers. 77 | Struct { 78 | /// `struct` 79 | token: Token![struct], 80 | /// Fields of struct 81 | fields: Fields, 82 | }, 83 | /// A [`syn::ItemType`], minus common parts 84 | Type { 85 | /// `type` 86 | token: Token![type], 87 | /// `=` 88 | eq_token: Token![=], 89 | /// Target type 90 | ty: Box, 91 | }, 92 | /// A [`syn::ItemUnion`], minus common parts 93 | Union { 94 | /// `union` 95 | token: Token![union], 96 | /// Fields of union 97 | fields: FieldsNamed, 98 | }, 99 | } 100 | 101 | impl ScopeItem { 102 | /// Take span of `enum`/`struct`/`type`/`union` token 103 | pub fn token_span(&self) -> Span { 104 | match self { 105 | ScopeItem::Enum { token, .. } => token.span, 106 | ScopeItem::Struct { token, .. } => token.span, 107 | ScopeItem::Type { token, .. } => token.span, 108 | ScopeItem::Union { token, .. } => token.span, 109 | } 110 | } 111 | } 112 | 113 | /// A module supporting `impl Self` syntax 114 | /// 115 | /// This type is used for parsing. Expansion should use [`Self::contents`] 116 | /// directly, ignoring all other fields. 117 | #[derive(Debug)] 118 | pub struct ScopeMod { 119 | /// Module declaration 120 | pub token: Token![mod], 121 | /// Module name 122 | pub ident: Ident, 123 | /// Braces 124 | pub brace: Brace, 125 | /// Contents 126 | pub contents: Scope, 127 | } 128 | 129 | /// Contents of `impl_scope!` 130 | /// 131 | /// `impl_scope!` input consists of one item (an `enum`, `struct`, `type` alias 132 | /// or `union`) followed by any number of implementations, and is parsed into 133 | /// this struct. 134 | /// 135 | /// On its own, `impl_scope!` provides `impl Self` syntax, with the following 136 | /// expansion done within [`Self::expand`] (after application [`ScopeAttr`] 137 | /// rules): 138 | /// 139 | /// - `impl Self { ... }` expands to `impl #impl_generics #ty_ident #ty_generics #where_clause { ... }` 140 | /// - `impl Self where #clause2 { ... }` expands similarly, but using the combined where clause 141 | /// 142 | /// The secondary utility of `impl_scope!` is to allow attribute expansion 143 | /// within itself via [`ScopeAttr`] rules. These rules may read the type item 144 | /// (which may include field initializers in the case of a struct), read 145 | /// accompanying implementations, and even modify them. 146 | #[derive(Debug)] 147 | pub struct Scope { 148 | /// Outer attributes on the item 149 | pub attrs: Vec, 150 | /// Optional `pub`, etc. 151 | pub vis: Visibility, 152 | /// Item identifier 153 | pub ident: Ident, 154 | /// Item generics 155 | pub generics: Generics, 156 | /// The item 157 | pub item: ScopeItem, 158 | /// Trailing semicolon (type alias and unit struct only) 159 | pub semi: Option, 160 | /// Implementation items 161 | pub impls: Vec, 162 | /// Output of [`ScopeAttr`] rules 163 | /// 164 | /// This does not contain any content from input, only content generated 165 | /// from [`ScopeAttr`] rules. It is appended to output as an item (usually 166 | /// a [`syn::ImplItem`]), after [`Self::impls`] items. 167 | pub generated: Vec, 168 | } 169 | 170 | impl Scope { 171 | /// Apply attribute rules 172 | /// 173 | /// The supplied `rules` are applied in the order of definition, and their 174 | /// attributes removed from the item. 175 | pub fn apply_attrs(&mut self, find_rule: impl Fn(&Path) -> Option<&'static dyn ScopeAttr>) { 176 | let mut applied: Vec<(Span, *const dyn ScopeAttr)> = Vec::new(); 177 | 178 | let mut i = 0; 179 | while i < self.attrs.len() { 180 | if let Some(rule) = find_rule(&self.attrs[i].path()) { 181 | let attr = self.attrs.remove(i); 182 | 183 | if !rule.support_repetition() { 184 | // We compare the fat pointer (including vtable address; 185 | // the data may be zero-sized and thus not unique). 186 | // We consider two rules the same when data pointers and 187 | // vtables both compare equal. 188 | let span = attr.span(); 189 | let ptr = rule as *const dyn ScopeAttr; 190 | if let Some(first) = applied.iter().find(|(_, p)| std::ptr::eq(*p, ptr)) { 191 | emit_error!(span, "repeated use of attribute not allowed"); 192 | emit_error!(first.0, "first usage here"); 193 | continue; 194 | } 195 | applied.push((span, ptr)); 196 | } 197 | 198 | if let Err(err) = rule.apply(attr, self) { 199 | emit_error!(err.span(), "{}", err); 200 | } 201 | continue; 202 | } 203 | 204 | i += 1; 205 | } 206 | } 207 | 208 | /// Expand `impl Self` 209 | /// 210 | /// This is done automatically by [`Self::expand`]. It may be called earlier 211 | /// by a [`ScopeAttr`] if required. Calling multiple times is harmless. 212 | pub fn expand_impl_self(&mut self) { 213 | for impl_ in self.impls.iter_mut() { 214 | if impl_.self_ty == parse_quote! { Self } { 215 | let mut ident = self.ident.clone(); 216 | ident.set_span(impl_.self_ty.span()); 217 | let (_, ty_generics, _) = self.generics.split_for_impl(); 218 | impl_.self_ty = parse_quote! { #ident #ty_generics }; 219 | extend_generics(&mut impl_.generics, &self.generics); 220 | } 221 | } 222 | } 223 | 224 | /// Generate the [`TokenStream`] 225 | /// 226 | /// This is a convenience function. It is valid to, instead, (1) call 227 | /// [`Self::expand_impl_self`], then (2) use the [`ToTokens`] impl on 228 | /// `Scope`. 229 | pub fn expand(mut self) -> TokenStream { 230 | self.expand_impl_self(); 231 | self.to_token_stream() 232 | } 233 | } 234 | 235 | mod parsing { 236 | use super::*; 237 | use crate::fields::parsing::data_struct; 238 | use syn::parse::{Parse, ParseStream}; 239 | use syn::spanned::Spanned; 240 | use syn::{braced, Error, Field, Lifetime, Path, TypePath, WhereClause}; 241 | 242 | impl Parse for ScopeModAttrs { 243 | fn parse(_input: ParseStream) -> Result { 244 | Ok(Self) 245 | } 246 | } 247 | 248 | impl Parse for ScopeMod { 249 | fn parse(input: ParseStream) -> Result { 250 | let inner; 251 | 252 | let token = input.parse()?; 253 | let ident = input.parse::()?; 254 | let brace = syn::braced!(inner in input); 255 | let contents: Scope = inner.parse()?; 256 | 257 | if ident != contents.ident { 258 | return Err(syn::Error::new( 259 | contents.ident.span(), 260 | "type name must match mod name", 261 | )); 262 | } 263 | 264 | Ok(ScopeMod { 265 | token, 266 | ident, 267 | brace, 268 | contents, 269 | }) 270 | } 271 | } 272 | 273 | impl Parse for Scope { 274 | fn parse(input: ParseStream) -> Result { 275 | let attrs = input.call(Attribute::parse_outer)?; 276 | let vis = input.parse::()?; 277 | 278 | enum Token { 279 | Enum(Token![enum]), 280 | Struct(Token![struct]), 281 | Type(Token![type]), 282 | Union(Token![union]), 283 | } 284 | let lookahead = input.lookahead1(); 285 | let token; 286 | if lookahead.peek(Token![enum]) { 287 | token = Token::Enum(input.parse()?); 288 | } else if lookahead.peek(Token![struct]) { 289 | token = Token::Struct(input.parse()?); 290 | } else if lookahead.peek(Token![type]) { 291 | token = Token::Type(input.parse()?); 292 | } else if lookahead.peek(Token![union]) { 293 | token = Token::Union(input.parse()?); 294 | } else { 295 | return Err(lookahead.error()); 296 | } 297 | 298 | let ident = input.parse::()?; 299 | let mut generics = input.parse::()?; 300 | 301 | let item; 302 | let mut semi = None; 303 | match token { 304 | Token::Enum(token) => { 305 | let (wc, brace, variants) = data_enum(&input)?; 306 | generics.where_clause = wc; 307 | item = ScopeItem::Enum { 308 | token, 309 | brace, 310 | variants, 311 | }; 312 | } 313 | Token::Struct(token) => { 314 | let (wc, fields, semi_token) = data_struct(&input)?; 315 | generics.where_clause = wc; 316 | semi = semi_token; 317 | item = ScopeItem::Struct { token, fields }; 318 | } 319 | Token::Type(token) => { 320 | let eq_token = input.parse()?; 321 | let ty = input.parse()?; 322 | let semi_token = input.parse()?; 323 | semi = Some(semi_token); 324 | item = ScopeItem::Type { 325 | token, 326 | eq_token, 327 | ty, 328 | }; 329 | } 330 | Token::Union(token) => { 331 | let (wc, fields) = data_union(&input)?; 332 | generics.where_clause = wc; 333 | item = ScopeItem::Union { token, fields }; 334 | } 335 | } 336 | 337 | let mut impls = Vec::new(); 338 | while !input.is_empty() { 339 | impls.push(parse_impl(&ident, &input)?); 340 | } 341 | 342 | Ok(Scope { 343 | attrs, 344 | vis, 345 | ident, 346 | generics, 347 | item, 348 | semi, 349 | impls, 350 | generated: vec![], 351 | }) 352 | } 353 | } 354 | 355 | fn parse_impl(in_ident: &Ident, input: ParseStream) -> Result { 356 | let mut attrs = input.call(Attribute::parse_outer)?; 357 | let defaultness: Option = input.parse()?; 358 | let unsafety: Option = input.parse()?; 359 | let impl_token: Token![impl] = input.parse()?; 360 | 361 | let has_generics = input.peek(Token![<]) 362 | && (input.peek2(Token![>]) 363 | || input.peek2(Token![#]) 364 | || (input.peek2(Ident) || input.peek2(Lifetime)) 365 | && (input.peek3(Token![:]) 366 | || input.peek3(Token![,]) 367 | || input.peek3(Token![>]) 368 | || input.peek3(Token![=])) 369 | || input.peek2(Token![const])); 370 | let mut generics: Generics = if has_generics { 371 | input.parse()? 372 | } else { 373 | Generics::default() 374 | }; 375 | 376 | let mut first_ty: Type = input.parse()?; 377 | let self_ty: Type; 378 | let trait_; 379 | 380 | let is_impl_for = input.peek(Token![for]); 381 | if is_impl_for { 382 | let for_token: Token![for] = input.parse()?; 383 | let mut first_ty_ref = &first_ty; 384 | while let Type::Group(ty) = first_ty_ref { 385 | first_ty_ref = &ty.elem; 386 | } 387 | if let Type::Path(_) = first_ty_ref { 388 | while let Type::Group(ty) = first_ty { 389 | first_ty = *ty.elem; 390 | } 391 | if let Type::Path(TypePath { qself: None, path }) = first_ty { 392 | trait_ = Some((None, path, for_token)); 393 | } else { 394 | unreachable!(); 395 | } 396 | } else { 397 | return Err(Error::new(for_token.span, "for without target trait")); 398 | } 399 | self_ty = input.parse()?; 400 | } else { 401 | trait_ = None; 402 | self_ty = first_ty; 403 | } 404 | 405 | generics.where_clause = input.parse()?; 406 | 407 | if self_ty != parse_quote! { Self } 408 | && !matches!(self_ty, Type::Path(TypePath { 409 | qself: None, 410 | path: Path { 411 | leading_colon: None, 412 | ref segments, 413 | } 414 | }) if segments.len() == 1 && segments.first().unwrap().ident == *in_ident) 415 | { 416 | return Err(Error::new( 417 | self_ty.span(), 418 | format!( 419 | "expected `Self` or `{0}` or `{0}<...>` or `Trait for Self`, etc", 420 | in_ident 421 | ), 422 | )); 423 | } 424 | 425 | let content; 426 | let brace_token = braced!(content in input); 427 | attrs.extend(Attribute::parse_inner(&content)?); 428 | 429 | let mut items = Vec::new(); 430 | while !content.is_empty() { 431 | items.push(content.parse()?); 432 | } 433 | 434 | Ok(ItemImpl { 435 | attrs, 436 | defaultness, 437 | unsafety, 438 | impl_token, 439 | generics, 440 | trait_, 441 | self_ty: Box::new(self_ty), 442 | brace_token, 443 | items, 444 | }) 445 | } 446 | 447 | pub fn data_enum( 448 | input: ParseStream, 449 | ) -> Result<(Option, Brace, Punctuated)> { 450 | let where_clause = input.parse()?; 451 | 452 | let content; 453 | let brace = braced!(content in input); 454 | let variants = content.parse_terminated(Variant::parse, Token![,])?; 455 | 456 | Ok((where_clause, brace, variants)) 457 | } 458 | 459 | pub fn data_union(input: ParseStream) -> Result<(Option, FieldsNamed)> { 460 | let where_clause = input.parse()?; 461 | let fields = parse_braced(input)?; 462 | Ok((where_clause, fields)) 463 | } 464 | 465 | pub(crate) fn parse_braced(input: ParseStream) -> Result { 466 | let content; 467 | let brace_token = braced!(content in input); 468 | let named = content.parse_terminated(Field::parse_named, Token![,])?; 469 | Ok(FieldsNamed { brace_token, named }) 470 | } 471 | } 472 | 473 | mod printing { 474 | use super::*; 475 | 476 | impl ToTokens for Scope { 477 | fn to_tokens(&self, tokens: &mut TokenStream) { 478 | tokens.append_all(self.attrs.iter()); 479 | self.vis.to_tokens(tokens); 480 | match &self.item { 481 | ScopeItem::Enum { token, .. } => token.to_tokens(tokens), 482 | ScopeItem::Struct { token, .. } => token.to_tokens(tokens), 483 | ScopeItem::Type { token, .. } => token.to_tokens(tokens), 484 | ScopeItem::Union { token, .. } => token.to_tokens(tokens), 485 | } 486 | self.ident.to_tokens(tokens); 487 | self.generics.to_tokens(tokens); 488 | match &self.item { 489 | ScopeItem::Enum { 490 | brace, variants, .. 491 | } => { 492 | self.generics.where_clause.to_tokens(tokens); 493 | brace.surround(tokens, |tokens| { 494 | variants.to_tokens(tokens); 495 | }); 496 | } 497 | ScopeItem::Struct { fields, .. } => match fields { 498 | Fields::Named(fields) => { 499 | self.generics.where_clause.to_tokens(tokens); 500 | fields.to_tokens(tokens); 501 | } 502 | Fields::Unnamed(fields) => { 503 | fields.to_tokens(tokens); 504 | self.generics.where_clause.to_tokens(tokens); 505 | } 506 | Fields::Unit => { 507 | self.generics.where_clause.to_tokens(tokens); 508 | } 509 | }, 510 | ScopeItem::Type { eq_token, ty, .. } => { 511 | self.generics.where_clause.to_tokens(tokens); 512 | eq_token.to_tokens(tokens); 513 | ty.to_tokens(tokens); 514 | } 515 | ScopeItem::Union { fields, .. } => { 516 | self.generics.where_clause.to_tokens(tokens); 517 | fields.to_tokens(tokens); 518 | } 519 | } 520 | if let Some(semi) = self.semi.as_ref() { 521 | semi.to_tokens(tokens); 522 | } 523 | 524 | tokens.append_all(self.impls.iter()); 525 | tokens.append_all(self.generated.iter()); 526 | } 527 | } 528 | } 529 | 530 | // Support impls on Self by replacing name and summing generics 531 | fn extend_generics(generics: &mut Generics, in_generics: &Generics) { 532 | if generics.lt_token.is_none() { 533 | debug_assert!(generics.params.is_empty()); 534 | debug_assert!(generics.gt_token.is_none()); 535 | generics.lt_token = in_generics.lt_token; 536 | generics.params = in_generics.params.clone(); 537 | generics.gt_token = in_generics.gt_token; 538 | } else if in_generics.lt_token.is_none() { 539 | debug_assert!(in_generics.params.is_empty()); 540 | debug_assert!(in_generics.gt_token.is_none()); 541 | } else { 542 | if !generics.params.empty_or_trailing() { 543 | generics.params.push_punct(Default::default()); 544 | } 545 | generics 546 | .params 547 | .extend(in_generics.params.clone().into_pairs()); 548 | } 549 | 550 | // Strip defaults which are legal on the struct but not on impls 551 | for param in &mut generics.params { 552 | match param { 553 | GenericParam::Type(p) => { 554 | p.eq_token = None; 555 | p.default = None; 556 | } 557 | GenericParam::Lifetime(_) => (), 558 | GenericParam::Const(p) => { 559 | p.eq_token = None; 560 | p.default = None; 561 | } 562 | } 563 | } 564 | 565 | if let Some(ref mut clause1) = generics.where_clause { 566 | if let Some(ref clause2) = in_generics.where_clause { 567 | if !clause1.predicates.empty_or_trailing() { 568 | clause1.predicates.push_punct(Default::default()); 569 | } 570 | clause1 571 | .predicates 572 | .extend(clause2.predicates.clone().into_pairs()); 573 | } 574 | } else { 575 | generics.where_clause = in_generics.where_clause.clone(); 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------