Import derive_more 0.99.17 upstream upstream/0.99.17
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 22 Mar 2023 03:47:12 +0000 (12:47 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 22 Mar 2023 03:47:12 +0000 (12:47 +0900)
70 files changed:
.cargo_vcs_info.json [new file with mode: 0644]
CHANGELOG.md [new file with mode: 0644]
Cargo.lock [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
src/add_assign_like.rs [new file with mode: 0644]
src/add_helpers.rs [new file with mode: 0644]
src/add_like.rs [new file with mode: 0644]
src/as_mut.rs [new file with mode: 0644]
src/as_ref.rs [new file with mode: 0644]
src/constructor.rs [new file with mode: 0644]
src/deref.rs [new file with mode: 0644]
src/deref_mut.rs [new file with mode: 0644]
src/display.rs [new file with mode: 0644]
src/error.rs [new file with mode: 0644]
src/from.rs [new file with mode: 0644]
src/from_str.rs [new file with mode: 0644]
src/index.rs [new file with mode: 0644]
src/index_mut.rs [new file with mode: 0644]
src/into.rs [new file with mode: 0644]
src/into_iterator.rs [new file with mode: 0644]
src/is_variant.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/mul_assign_like.rs [new file with mode: 0644]
src/mul_helpers.rs [new file with mode: 0644]
src/mul_like.rs [new file with mode: 0644]
src/not_like.rs [new file with mode: 0644]
src/parsing.rs [new file with mode: 0644]
src/sum_like.rs [new file with mode: 0644]
src/try_into.rs [new file with mode: 0644]
src/unwrap.rs [new file with mode: 0644]
src/utils.rs [new file with mode: 0644]
tests/add.rs [new file with mode: 0644]
tests/add_assign.rs [new file with mode: 0644]
tests/as_mut.rs [new file with mode: 0644]
tests/as_ref.rs [new file with mode: 0644]
tests/boats_display_derive.rs [new file with mode: 0644]
tests/constructor.rs [new file with mode: 0644]
tests/deref.rs [new file with mode: 0644]
tests/deref_mut.rs [new file with mode: 0644]
tests/display.rs [new file with mode: 0644]
tests/error/derives_for_enums_with_source.rs [new file with mode: 0644]
tests/error/derives_for_generic_enums_with_source.rs [new file with mode: 0644]
tests/error/derives_for_generic_structs_with_source.rs [new file with mode: 0644]
tests/error/derives_for_structs_with_source.rs [new file with mode: 0644]
tests/error/mod.rs [new file with mode: 0644]
tests/error/nightly/derives_for_enums_with_backtrace.rs [new file with mode: 0644]
tests/error/nightly/derives_for_generic_enums_with_backtrace.rs [new file with mode: 0644]
tests/error/nightly/derives_for_generic_structs_with_backtrace.rs [new file with mode: 0644]
tests/error/nightly/derives_for_structs_with_backtrace.rs [new file with mode: 0644]
tests/error/nightly/mod.rs [new file with mode: 0644]
tests/error_tests.rs [new file with mode: 0644]
tests/from.rs [new file with mode: 0644]
tests/from_str.rs [new file with mode: 0644]
tests/generics.rs [new file with mode: 0644]
tests/index.rs [new file with mode: 0644]
tests/index_mut.rs [new file with mode: 0644]
tests/into.rs [new file with mode: 0644]
tests/into_iterator.rs [new file with mode: 0644]
tests/is_variant.rs [new file with mode: 0644]
tests/lib.rs [new file with mode: 0644]
tests/mul.rs [new file with mode: 0644]
tests/mul_assign.rs [new file with mode: 0644]
tests/no_std.rs [new file with mode: 0644]
tests/not.rs [new file with mode: 0644]
tests/sum.rs [new file with mode: 0644]
tests/try_into.rs [new file with mode: 0644]
tests/unwrap.rs [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..d021a7e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "718fbb6757acca99479a8b47b2a2336d07873ad2"
+  }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644 (file)
index 0000000..a04b013
--- /dev/null
@@ -0,0 +1,246 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+
+## 0.99.10 - 2020-??-??
+
+### Improvements
+
+- `From` supports additional types for conversion: `#[from(types(u8, u16))]`.
+
+
+## 0.99.7 - 2020-05-16
+
+### Fixes
+
+- Fix generic derives for `MulAssign`
+
+### Improvements
+
+- When specifying specific features of the crate to only enable specific
+    derives, the `extra-traits` feature of  `syn` is not always enabled
+    when those the specified features do not require it. This should speed up
+    compile time of `syn` when this feature is not needed.
+
+
+## 0.99.6 - 2020-05-13
+
+### Improvements
+
+- Make sure output of derives is deterministic, for better support in
+    rust-analyzer
+
+
+## 0.99.5 - 2020-03-28
+
+### New features
+
+- Support for deriving `Error`!!! (many thanks to @ffuugoo and @tyranron)
+
+### Fixes
+
+- Fix generic bounds for `Deref` and `DerefMut` with `forward`, i.e. put `Deref`
+  bound on whole type, so on `where Box<T>: Deref` instead of on `T: Deref`.
+  ([#107](https://github.com/JelteF/derive_more/issues/114))
+
+- The `tests` directory is now correctly included in the crate (requested by
+    Debian package maintainers)
+
+## 0.99.4 - 2020-03-28
+
+Note: This version is yanked, because quickly after release it was found out
+tests did not run in CI.
+
+## 0.99.3 - 2020-02-19
+
+### Fixes
+
+- Fix generic bounds for `Deref` and `DerefMut` with no `forward`, i.e. no bounds
+    are necessary. ([#107](https://github.com/JelteF/derive_more/issues/114))
+
+
+## 0.99.2 - 2019-11-17
+
+### Fixes
+
+- Hotfix for a regression in allowed `Display` derives using `#` flag, such as
+    `{:#b}` ([#107](https://github.com/JelteF/derive_more/issues/107))
+
+## 0.99.1 - 2019-11-12
+
+### Fixes
+
+- Hotfix for a regression in allowed `From` derives
+    ([#105](https://github.com/JelteF/derive_more/issues/105))
+
+## 0.99.0 - 2019-11-11
+
+This release is a huge milestone for this library.
+Lot's of new derives are implemented and a ton of attributes are added for
+configuration purposes.
+These attributes will allow future releases to add features/options without
+breaking backwards compatibility.
+This is why the next release with breaking changes is planned to be 1.0.0.
+
+### Breaking changes
+
+- Requires Rust 1.36+
+- When using in a Rust 2015 crate, you should add `extern crate core` to your
+  code.
+- `no_std` feature is removed, the library now supports `no_std` without having
+  to configure any features.
+- `Deref` derives now dereference to the type in the newtype. So if you have
+  `MyBox(Box<i32>)`, dereferencing it will result in a `Box<i32>` not an `i32`.
+  To get the old behaviour of forwarding the dereference you can add the
+  `#[deref(forward)]` attribute on the struct or field.
+
+### New features
+
+- Derives for `AsRef`, `AsMut`, `Sum`, `Product`, `IntoIterator`.
+- Choosing the field of a struct for which to derive the newtype derive.
+- Ignoring variants of enums when deriving `From`, by using `#[from(ignore)]`.
+- Add `#[from(forward)]` attribute for `From` derives. This forwards the `from`
+  calls to the fields themselves. So if your field is an `i64` you can call from
+  on an `i32` and it will work.
+- Add `#[mul(forward)]` and `#[mul_assign(forward)]`, which implement `Mul` and
+  `MulAssign` with the semantics as if they were `Add`/`AddAssign`.
+- You can use features to cut down compile time of the crate by only compiling
+  the code needed for the derives that you use. (see Cargo.toml for the
+  features, by default they are all on)
+- Add `#[into(owned, ref, ref_mut)]` and `#[try_into(owned, ref, ref_mut)]`
+  attributes. These cause the `Into` and `TryInto` derives to also implement
+  derives that return references to the inner fields.
+- Make `no_std` work out of the box
+- Allow `#[display(fmt="some shared display text for all enum variants {}")]`
+  attribute on enum.
+- Better bounds inference of `Display` trait.
+
+### Other things
+
+- Remove dependency on `regex` to cut down compile time.
+- Use `syn` 1.0
+
+## 0.15.0 - 2019-06-08
+
+- Automatic detection of traits needed for `Display` format strings
+
+## 0.14.0 - 2019-02-02
+
+- Added `no_std` support
+- Suppress `unused_variables` warnings in derives
+
+## 0.13.0 - 2018-10-19
+
+- Updated to `syn` v0.15
+- Extended Display-like derives to support custom formats
+
+## 0.12.0 - 2018-09-19
+
+### Changed
+
+- Updated to `syn` v0.14, `quote` v0.6 and `proc-macro2` v0.4
+
+## 0.11.0 - 2018-05-12
+
+### Changed
+
+- Updated to latest version of `syn` and `quote`
+
+### Fixed
+
+- Changed some URLs in the docs so they were correct on crates.io and docs.rs
+- The `Result` type is now referenced in the derives using its absolute path
+  (`::std::result::Result`) to make sure that the derives don't accidentally use
+  another `Result` type that is in scope.
+
+## 0.10.0 - 2018-03-29
+
+### Added
+
+- Allow deriving of `TryInto`
+- Allow deriving of `Deref`
+- Allow deriving of `DerefMut`
+
+## 0.9.0 - 2018-03-18
+
+### Added
+
+- Allow deriving of `Display`, `Binary`, `Octal`, `LowerHex`, `UpperHex`, `LowerExp`, `UpperExp`, `Pointer`
+- Allow deriving of `Index`
+- Allow deriving of `IndexMut`
+
+### Fixed
+
+- Allow cross crate inlining of derived methods
+
+### Internal changes
+
+- Fix most `clippy` warnings
+
+## 0.8.0 - 2018-03-10
+
+### Added
+
+- Allow deriving of `FromStr`
+
+### Changed
+
+- Updated to latest version of `syn` and `quote`
+
+## 0.7.1 - 2018-01-25
+
+### Fixed
+
+- Add `#[allow(missing_docs)]` to the Constructor definition
+
+### Internal changes
+
+- Run `rustfmt` on the code
+
+## 0.7.0 - 2017-07-25
+
+### Changed
+
+- Changed code to work with newer version of the `syn` library.
+
+## 0.6.2 - 2017-04-23
+
+### Changed
+
+- Deriving `From`, `Into` and `Constructor` now works for empty structs.
+
+## 0.6.1 - 2017-03-08
+
+### Changed
+
+- The `new()` method that is created when deriving `Constructor` is now public.
+  This makes it a lot more useful.
+
+## 0.6.0 - 2017-02-20
+
+### Added
+
+- Derives for `Into`, `Constructor` and `MulAssign`-like
+
+### Changed
+
+- `From` is now derived for enum variants with multiple fields.
+
+### Fixed
+
+- Derivations now support generics.
+
+## 0.5.0 - 2017-02-02
+
+### Added
+
+- Lots of docs.
+- Derives for `Neg`-like and `AddAssign`-like.
+
+### Changed
+
+- `From` can now be derived for structs with multiple fields.
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644 (file)
index 0000000..4bdc3f3
--- /dev/null
@@ -0,0 +1,86 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+dependencies = [
+ "convert_case",
+ "peg",
+ "proc-macro2",
+ "quote 1.0.10",
+ "rustc_version",
+ "syn",
+]
+
+[[package]]
+name = "peg"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40df12dde1d836ed2a4c3bfc2799797e3abaf807d97520d28d6e3f3bf41a5f85"
+dependencies = [
+ "quote 0.3.15",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
+
+[[package]]
+name = "quote"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
+
+[[package]]
+name = "syn"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
+dependencies = [
+ "proc-macro2",
+ "quote 1.0.10",
+ "unicode-xid",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..ce08389
--- /dev/null
@@ -0,0 +1,216 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "derive_more"
+version = "0.99.17"
+authors = ["Jelte Fennema <github-tech@jeltef.nl>"]
+include = ["src/**/*.rs", "Cargo.toml", "LICENSE", "README.md", "CHANGELOG.md", "tests/**/*.rs"]
+autotests = true
+description = "Adds #[derive(x)] macros for more traits"
+documentation = "https://jeltef.github.io/derive_more/derive_more/"
+readme = "README.md"
+keywords = ["derive", "Add", "From", "Display", "IntoIterator"]
+categories = ["development-tools", "development-tools::procedural-macro-helpers", "no-std"]
+license = "MIT"
+repository = "https://github.com/JelteF/derive_more"
+
+[lib]
+name = "derive_more"
+proc-macro = true
+
+[[example]]
+name = "deny_missing_docs"
+path = "examples/deny_missing_docs.rs"
+required-features = ["add_assign", "add", "as_mut", "as_ref", "constructor", "deref", "deref_mut", "display", "from", "from_str", "index", "index_mut", "into", "mul_assign", "mul", "not", "try_into", "is_variant"]
+
+[[test]]
+name = "add_assign"
+path = "tests/add_assign.rs"
+required-features = ["add_assign"]
+
+[[test]]
+name = "add"
+path = "tests/add.rs"
+required-features = ["add"]
+
+[[test]]
+name = "as_mut"
+path = "tests/as_mut.rs"
+required-features = ["as_mut"]
+
+[[test]]
+name = "as_ref"
+path = "tests/as_ref.rs"
+required-features = ["as_ref"]
+
+[[test]]
+name = "boats_display_derive"
+path = "tests/boats_display_derive.rs"
+required-features = ["display"]
+
+[[test]]
+name = "constructor"
+path = "tests/constructor.rs"
+required-features = ["constructor"]
+
+[[test]]
+name = "deref"
+path = "tests/deref.rs"
+required-features = ["deref"]
+
+[[test]]
+name = "deref_mut"
+path = "tests/deref_mut.rs"
+required-features = ["deref_mut"]
+
+[[test]]
+name = "display"
+path = "tests/display.rs"
+required-features = ["display"]
+
+[[test]]
+name = "error"
+path = "tests/error_tests.rs"
+required-features = ["error"]
+
+[[test]]
+name = "from"
+path = "tests/from.rs"
+required-features = ["from"]
+
+[[test]]
+name = "from_str"
+path = "tests/from_str.rs"
+required-features = ["from_str"]
+
+[[test]]
+name = "index_mut"
+path = "tests/index_mut.rs"
+required-features = ["index_mut"]
+
+[[test]]
+name = "index"
+path = "tests/index.rs"
+required-features = ["index"]
+
+[[test]]
+name = "into"
+path = "tests/into.rs"
+required-features = ["into"]
+
+[[test]]
+name = "into_iterator"
+path = "tests/into_iterator.rs"
+required-features = ["into_iterator"]
+
+[[test]]
+name = "mul_assign"
+path = "tests/mul_assign.rs"
+required-features = ["mul_assign"]
+
+[[test]]
+name = "mul"
+path = "tests/mul.rs"
+required-features = ["mul"]
+
+[[test]]
+name = "not"
+path = "tests/not.rs"
+required-features = ["not"]
+
+[[test]]
+name = "sum"
+path = "tests/sum.rs"
+required-features = ["sum"]
+
+[[test]]
+name = "try_into"
+path = "tests/try_into.rs"
+required-features = ["try_into"]
+
+[[test]]
+name = "is_variant"
+path = "tests/is_variant.rs"
+required-features = ["is_variant"]
+
+[[test]]
+name = "unwrap"
+path = "tests/unwrap.rs"
+required-features = ["unwrap"]
+
+[[test]]
+name = "no_std"
+path = "tests/no_std.rs"
+required-features = ["add_assign", "add", "as_mut", "as_ref", "constructor", "deref", "deref_mut", "display", "from", "from_str", "index", "index_mut", "into", "mul_assign", "mul", "not", "sum", "try_into", "is_variant"]
+
+[[test]]
+name = "generics"
+path = "tests/generics.rs"
+required-features = ["add_assign", "add", "as_mut", "as_ref", "constructor", "deref", "deref_mut", "display", "from", "from_str", "index", "index_mut", "into", "mul_assign", "mul", "not", "try_into", "is_variant"]
+
+[[test]]
+name = "lib"
+path = "tests/lib.rs"
+required-features = ["add_assign", "add", "as_mut", "as_ref", "constructor", "deref", "deref_mut", "display", "from", "from_str", "index", "index_mut", "into", "mul_assign", "mul", "not", "try_into", "is_variant"]
+[dependencies.convert_case]
+version = "0.4"
+optional = true
+
+[dependencies.proc-macro2]
+version = "1.0"
+
+[dependencies.quote]
+version = "1.0"
+
+[dependencies.syn]
+version = "1.0.3"
+[build-dependencies.peg]
+version = "0.5"
+optional = true
+
+[build-dependencies.rustc_version]
+version = "0.4"
+optional = true
+
+[features]
+add = []
+add_assign = []
+as_mut = []
+as_ref = []
+constructor = []
+default = ["add_assign", "add", "as_mut", "as_ref", "constructor", "deref", "deref_mut", "display", "error", "from", "from_str", "index", "index_mut", "into", "into_iterator", "iterator", "mul_assign", "mul", "not", "sum", "try_into", "is_variant", "unwrap"]
+deref = []
+deref_mut = []
+display = ["syn/extra-traits"]
+error = ["syn/extra-traits"]
+from = ["syn/extra-traits"]
+from_str = []
+generate-parsing-rs = ["peg"]
+index = []
+index_mut = []
+into = ["syn/extra-traits"]
+into_iterator = []
+is_variant = ["convert_case"]
+iterator = []
+mul = ["syn/extra-traits"]
+mul_assign = ["syn/extra-traits"]
+nightly = []
+not = ["syn/extra-traits"]
+sum = []
+testing-helpers = ["rustc_version"]
+track-caller = []
+try_into = ["syn/extra-traits"]
+unwrap = ["convert_case", "rustc_version"]
+[badges.github]
+repository = "JelteF/derive_more"
+workflow = "CI"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..458267d
--- /dev/null
@@ -0,0 +1,311 @@
+[package]
+name = "derive_more"
+version = "0.99.17"
+description = "Adds #[derive(x)] macros for more traits"
+authors = ["Jelte Fennema <github-tech@jeltef.nl>"]
+license = "MIT"
+repository = "https://github.com/JelteF/derive_more"
+documentation = "https://jeltef.github.io/derive_more/derive_more/"
+edition = "2018"
+
+readme = "README.md"
+keywords = ["derive", "Add", "From", "Display", "IntoIterator"]
+categories = ["development-tools", "development-tools::procedural-macro-helpers", "no-std"]
+
+include = [
+    "src/**/*.rs",
+    "Cargo.toml",
+    "LICENSE",
+    "README.md",
+    "CHANGELOG.md",
+    "tests/**/*.rs", # debian packaging wants this
+]
+autotests = true
+
+[lib]
+name = "derive_more"
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = "1.0.3"
+convert_case = { version = "0.4", optional = true}
+
+[build-dependencies]
+peg = { version = "0.5", optional = true }
+rustc_version = { version = "0.4", optional = true }
+
+[badges]
+github = { repository = "JelteF/derive_more", workflow = "CI" }
+
+[features]
+nightly = []
+add_assign = []
+add = []
+as_mut = []
+as_ref = []
+constructor = []
+deref = []
+deref_mut = []
+display = ["syn/extra-traits"]
+error = ["syn/extra-traits"]
+from = ["syn/extra-traits"]
+from_str = []
+index = []
+index_mut = []
+into = ["syn/extra-traits"]
+into_iterator = []
+iterator = []
+mul_assign = ["syn/extra-traits"]
+mul = ["syn/extra-traits"]
+not = ["syn/extra-traits"]
+sum = []
+try_into = ["syn/extra-traits"]
+generate-parsing-rs = ["peg"]
+testing-helpers = ["rustc_version"]
+is_variant = ["convert_case"]
+unwrap = ["convert_case", "rustc_version"]
+# Feature that requires post-MSRV Rust version,
+# of 1.46. We perform rustc version detection in the
+# build script for features that require this.
+# (Currently just `unwrap`.)
+track-caller = []
+
+default = [
+    "add_assign",
+    "add",
+    "as_mut",
+    "as_ref",
+    "constructor",
+    "deref",
+    "deref_mut",
+    "display",
+    "error",
+    "from",
+    "from_str",
+    "index",
+    "index_mut",
+    "into",
+    "into_iterator",
+    "iterator",
+    "mul_assign",
+    "mul",
+    "not",
+    "sum",
+    "try_into",
+    "is_variant",
+    "unwrap"
+]
+
+[[test]]
+name = "add_assign"
+path = "tests/add_assign.rs"
+required-features = ["add_assign"]
+
+[[test]]
+name = "add"
+path = "tests/add.rs"
+required-features = ["add"]
+
+[[test]]
+name = "as_mut"
+path = "tests/as_mut.rs"
+required-features = ["as_mut"]
+
+[[test]]
+name = "as_ref"
+path = "tests/as_ref.rs"
+required-features = ["as_ref"]
+
+[[test]]
+name = "boats_display_derive"
+path = "tests/boats_display_derive.rs"
+required-features = ["display"]
+
+[[test]]
+name = "constructor"
+path = "tests/constructor.rs"
+required-features = ["constructor"]
+
+[[test]]
+name = "deref"
+path = "tests/deref.rs"
+required-features = ["deref"]
+
+[[test]]
+name = "deref_mut"
+path = "tests/deref_mut.rs"
+required-features = ["deref_mut"]
+
+[[test]]
+name = "display"
+path = "tests/display.rs"
+required-features = ["display"]
+
+[[test]]
+name = "error"
+path = "tests/error_tests.rs"
+required-features = ["error"]
+
+[[test]]
+name = "from"
+path = "tests/from.rs"
+required-features = ["from"]
+
+[[test]]
+name = "from_str"
+path = "tests/from_str.rs"
+required-features = ["from_str"]
+
+[[test]]
+name = "index_mut"
+path = "tests/index_mut.rs"
+required-features = ["index_mut"]
+
+[[test]]
+name = "index"
+path = "tests/index.rs"
+required-features = ["index"]
+
+[[test]]
+name = "into"
+path = "tests/into.rs"
+required-features = ["into"]
+
+[[test]]
+name = "into_iterator"
+path = "tests/into_iterator.rs"
+required-features = ["into_iterator"]
+
+[[test]]
+name = "mul_assign"
+path = "tests/mul_assign.rs"
+required-features = ["mul_assign"]
+
+[[test]]
+name = "mul"
+path = "tests/mul.rs"
+required-features = ["mul"]
+
+[[test]]
+name = "not"
+path = "tests/not.rs"
+required-features = ["not"]
+
+[[test]]
+name = "sum"
+path = "tests/sum.rs"
+required-features = ["sum"]
+
+[[test]]
+name = "try_into"
+path = "tests/try_into.rs"
+required-features = ["try_into"]
+
+[[test]]
+name = "is_variant"
+path = "tests/is_variant.rs"
+required-features = ["is_variant"]
+
+[[test]]
+name = "unwrap"
+path = "tests/unwrap.rs"
+required-features = ["unwrap"]
+
+[[test]]
+name = "no_std"
+path = "tests/no_std.rs"
+required-features = [
+    "add_assign",
+    "add",
+    "as_mut",
+    "as_ref",
+    "constructor",
+    "deref",
+    "deref_mut",
+    "display",
+    "from",
+    "from_str",
+    "index",
+    "index_mut",
+    "into",
+    "mul_assign",
+    "mul",
+    "not",
+    "sum",
+    "try_into",
+    "is_variant",
+]
+
+[[test]]
+name = "generics"
+path = "tests/generics.rs"
+required-features = [
+    "add_assign",
+    "add",
+    "as_mut",
+    "as_ref",
+    "constructor",
+    "deref",
+    "deref_mut",
+    "display",
+    "from",
+    "from_str",
+    "index",
+    "index_mut",
+    "into",
+    "mul_assign",
+    "mul",
+    "not",
+    "try_into",
+    "is_variant",
+]
+
+[[test]]
+name = "lib"
+path = "tests/lib.rs"
+required-features = [
+    "add_assign",
+    "add",
+    "as_mut",
+    "as_ref",
+    "constructor",
+    "deref",
+    "deref_mut",
+    "display",
+    "from",
+    "from_str",
+    "index",
+    "index_mut",
+    "into",
+    "mul_assign",
+    "mul",
+    "not",
+    "try_into",
+    "is_variant",
+]
+
+[[example]]
+name = "deny_missing_docs"
+path = "examples/deny_missing_docs.rs"
+required-features = [
+    "add_assign",
+    "add",
+    "as_mut",
+    "as_ref",
+    "constructor",
+    "deref",
+    "deref_mut",
+    "display",
+    "from",
+    "from_str",
+    "index",
+    "index_mut",
+    "into",
+    "mul_assign",
+    "mul",
+    "not",
+    "try_into",
+    "is_variant",
+]
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..602cf05
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Jelte Fennema
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..507148d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,183 @@
+# `derive_more`
+
+[![Build Status](https://github.com/JelteF/derive_more/workflows/CI/badge.svg)](https://github.com/JelteF/derive_more/actions)
+[![Latest Version](https://img.shields.io/crates/v/derive_more.svg)](https://crates.io/crates/derive_more)
+[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://jeltef.github.io/derive_more/derive_more/)
+[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/JelteF/derive_more/master/LICENSE)
+[![Rust 1.36+](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html)
+
+Rust has lots of builtin traits that are implemented for its basic types, such
+as `Add`, `Not`, `From` or `Display`.
+However, when wrapping these types inside your own structs or enums you lose the
+implementations of these traits and are required to recreate them.
+This is especially annoying when your own structures are very simple, such as
+when using the commonly advised newtype pattern (e.g. `MyInt(i32)`).
+
+This library tries to remove these annoyances and the corresponding boilerplate code.
+It does this by allowing you to derive lots of commonly used traits for both structs and enums.
+
+## Example code
+
+By using this library the following code just works:
+
+```rust
+extern crate derive_more;
+use derive_more::{Add, Display, From, Into};
+
+#[derive(PartialEq, From, Add)]
+struct MyInt(i32);
+
+#[derive(PartialEq, From, Into)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(PartialEq, From, Add, Display)]
+enum MyEnum {
+    #[display(fmt = "int: {}", _0)]
+    Int(i32),
+    Uint(u32),
+    #[display(fmt = "nothing")]
+    Nothing,
+}
+
+assert!(MyInt(11) == MyInt(5) + 6.into());
+assert!((5, 6) == Point2D { x: 5, y: 6 }.into());
+assert!(MyEnum::Int(15) == (MyEnum::Int(8) + 7.into()).unwrap());
+assert!(MyEnum::Int(15).to_string() == "int: 15");
+assert!(MyEnum::Uint(42).to_string() == "42");
+assert!(MyEnum::Nothing.to_string() == "nothing");
+```
+
+## The derivable traits
+
+Below are all the traits that you can derive using this library.
+Some trait derivations are so similar that the further documentation will only show a single one
+of them.
+You can recognize these by the "-like" suffix in their name.
+The trait name before that will be the only one that is used throughout the further
+documentation.
+
+It is important to understand what code gets generated when using one of the
+derives from this crate.
+That is why the links below explain what code gets generated for a trait for
+each group from before.
+
+You can use the [`cargo-expand`] utility to see the exact code that is generated
+for your specific type.
+This will show you your code with all macros and derives expanded.
+
+**NOTE**: You still have to derive each trait separately. So `#[derive(Mul)]` doesn't
+automatically derive `Div` as well. To derive both you should do `#[derive(Mul, Div)]`
+
+### Conversion traits
+
+These are traits that are used to convert automatically between types.
+
+1. [`From`]
+2. [`Into`]
+3. [`FromStr`]
+4. [`TryInto`]
+5. [`IntoIterator`]
+6. [`AsRef`]
+7. [`AsMut`]
+
+### Formatting traits
+
+These traits are used for converting a struct to a string in different ways.
+
+1. [`Display`-like], contains `Display`, `DebugCustom`, `Binary`, `Octal`,
+   `LowerHex`, `UpperHex`, `LowerExp`, `UpperExp`, `Pointer`
+
+### Error-handling traits
+These traits are used to define error-types.
+
+1. [`Error`]
+
+### Operators
+
+These are traits that can be used for operator overloading.
+
+1. [`Index`]
+2. [`Deref`]
+3. [`Not`-like], contains `Not` and `Neg`
+4. [`Add`-like], contains `Add`, `Sub`, `BitAnd`, `BitOr`, `BitXor`
+5. [`Mul`-like], contains `Mul`, `Div`, `Rem`, `Shr` and `Shl`
+3. [`Sum`-like], contains `Sum` and `Product`
+6. [`IndexMut`]
+7. [`DerefMut`]
+8. [`AddAssign`-like], contains `AddAssign`, `SubAssign`, `BitAndAssign`,
+   `BitOrAssign` and `BitXorAssign`
+9. [`MulAssign`-like], contains `MulAssign`, `DivAssign`, `RemAssign`,
+   `ShrAssign` and `ShlAssign`
+
+### Static methods
+
+These don't derive traits, but derive static methods instead.
+
+1. [`Constructor`], this derives a `new` method that can be used as a constructor.
+   This is very basic if you need more customization for your constructor, check
+   out the [`derive-new`] crate.
+2. [`IsVariant`], for each variant `foo` of an enum type, derives a `is_foo` method.
+3. [`Unwrap`], for each variant `foo` of an enum type, derives an `unwrap_foo` method.
+
+## Generated code
+
+## Installation
+
+This library requires Rust 1.36 or higher and it supports `no_std` out of the box.
+Then add the following to `Cargo.toml`:
+
+```toml
+[dependencies]
+derive_more = "0.99.0"
+# You can specifiy the types of derives that you need for less time spent
+# compiling. For the full list of features see this crate its Cargo.toml.
+default-features = false
+features = ["from", "add", "iterator"]
+```
+
+And this to the top of your Rust file for Rust 2018:
+
+```rust
+extern crate derive_more;
+// use the derives that you want in the file
+use derive_more::{Add, Display, From};
+```
+If you're still using Rust 2015 you should add this instead:
+```rust
+extern crate core;
+#[macro_use]
+extern crate derive_more;
+```
+
+[`cargo-expand`]: https://github.com/dtolnay/cargo-expand
+[`derive-new`]: https://github.com/nrc/derive-new
+
+[`From`]: https://jeltef.github.io/derive_more/derive_more/from.html
+[`Into`]: https://jeltef.github.io/derive_more/derive_more/into.html
+[`FromStr`]: https://jeltef.github.io/derive_more/derive_more/from_str.html
+[`TryInto`]: https://jeltef.github.io/derive_more/derive_more/try_into.html
+[`IntoIterator`]: https://jeltef.github.io/derive_more/derive_more/into_iterator.html
+[`AsRef`]: https://jeltef.github.io/derive_more/derive_more/as_ref.html
+[`AsMut`]: https://jeltef.github.io/derive_more/derive_more/as_mut.html
+
+[`Display`-like]: https://jeltef.github.io/derive_more/derive_more/display.html
+
+[`Error`]: https://jeltef.github.io/derive_more/derive_more/error.html
+
+[`Index`]: https://jeltef.github.io/derive_more/derive_more/index_op.html
+[`Deref`]: https://jeltef.github.io/derive_more/derive_more/deref.html
+[`Not`-like]: https://jeltef.github.io/derive_more/derive_more/not.html
+[`Add`-like]: https://jeltef.github.io/derive_more/derive_more/add.html
+[`Mul`-like]: https://jeltef.github.io/derive_more/derive_more/mul.html
+[`Sum`-like]: https://jeltef.github.io/derive_more/derive_more/sum.html
+[`IndexMut`]: https://jeltef.github.io/derive_more/derive_more/index_mut.html
+[`DerefMut`]: https://jeltef.github.io/derive_more/derive_more/deref_mut.html
+[`AddAssign`-like]: https://jeltef.github.io/derive_more/derive_more/add_assign.html
+[`MulAssign`-like]: https://jeltef.github.io/derive_more/derive_more/mul_assign.html
+
+[`Constructor`]: https://jeltef.github.io/derive_more/derive_more/constructor.html
+[`IsVariant`]: https://jeltef.github.io/derive_more/derive_more/is_variant.html
+[`Unwrap`]: https://jeltef.github.io/derive_more/derive_more/unwrap.html
diff --git a/src/add_assign_like.rs b/src/add_assign_like.rs
new file mode 100644 (file)
index 0000000..60a7432
--- /dev/null
@@ -0,0 +1,41 @@
+use crate::add_helpers::{struct_exprs, tuple_exprs};
+use crate::utils::{add_extra_ty_param_bound_op, named_to_vec, unnamed_to_vec};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{Data, DeriveInput, Fields, Ident};
+
+pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream {
+    let trait_ident = Ident::new(trait_name, Span::call_site());
+    let method_name = trait_name.to_string();
+    let method_name = method_name.trim_end_matches("Assign");
+    let method_name = method_name.to_lowercase();
+    let method_ident = Ident::new(&(method_name + "_assign"), Span::call_site());
+    let input_type = &input.ident;
+
+    let generics = add_extra_ty_param_bound_op(&input.generics, &trait_ident);
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let exprs = match input.data {
+        Data::Struct(ref data_struct) => match data_struct.fields {
+            Fields::Unnamed(ref fields) => {
+                tuple_exprs(&unnamed_to_vec(fields), &method_ident)
+            }
+            Fields::Named(ref fields) => {
+                struct_exprs(&named_to_vec(fields), &method_ident)
+            }
+            _ => panic!("Unit structs cannot use derive({})", trait_name),
+        },
+
+        _ => panic!("Only structs can use derive({})", trait_name),
+    };
+
+    quote!(
+        impl#impl_generics ::core::ops::#trait_ident for #input_type#ty_generics #where_clause {
+            #[inline]
+            fn #method_ident(&mut self, rhs: #input_type#ty_generics) {
+                #(#exprs;
+                  )*
+            }
+        }
+    )
+}
diff --git a/src/add_helpers.rs b/src/add_helpers.rs
new file mode 100644 (file)
index 0000000..34f3d2a
--- /dev/null
@@ -0,0 +1,28 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{Field, Ident, Index};
+
+pub fn tuple_exprs(fields: &[&Field], method_ident: &Ident) -> Vec<TokenStream> {
+    let mut exprs = vec![];
+
+    for i in 0..fields.len() {
+        let i = Index::from(i);
+        // generates `self.0.add(rhs.0)`
+        let expr = quote!(self.#i.#method_ident(rhs.#i));
+        exprs.push(expr);
+    }
+    exprs
+}
+
+pub fn struct_exprs(fields: &[&Field], method_ident: &Ident) -> Vec<TokenStream> {
+    let mut exprs = vec![];
+
+    for field in fields {
+        // It's safe to unwrap because struct fields always have an identifier
+        let field_id = field.ident.as_ref().unwrap();
+        // generates `x: self.x.add(rhs.x)`
+        let expr = quote!(self.#field_id.#method_ident(rhs.#field_id));
+        exprs.push(expr)
+    }
+    exprs
+}
diff --git a/src/add_like.rs b/src/add_like.rs
new file mode 100644 (file)
index 0000000..5d435f6
--- /dev/null
@@ -0,0 +1,139 @@
+use crate::add_helpers::{struct_exprs, tuple_exprs};
+use crate::utils::{
+    add_extra_type_param_bound_op_output, field_idents, named_to_vec, numbered_vars,
+    unnamed_to_vec,
+};
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, ToTokens};
+use std::iter;
+use syn::{Data, DataEnum, DeriveInput, Field, Fields, Ident};
+
+pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream {
+    let trait_name = trait_name.trim_end_matches("Self");
+    let trait_ident = Ident::new(trait_name, Span::call_site());
+    let method_name = trait_name.to_lowercase();
+    let method_ident = Ident::new(&method_name, Span::call_site());
+    let input_type = &input.ident;
+
+    let generics = add_extra_type_param_bound_op_output(&input.generics, &trait_ident);
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let (output_type, block) = match input.data {
+        Data::Struct(ref data_struct) => match data_struct.fields {
+            Fields::Unnamed(ref fields) => (
+                quote!(#input_type#ty_generics),
+                tuple_content(input_type, &unnamed_to_vec(fields), &method_ident),
+            ),
+            Fields::Named(ref fields) => (
+                quote!(#input_type#ty_generics),
+                struct_content(input_type, &named_to_vec(fields), &method_ident),
+            ),
+            _ => panic!("Unit structs cannot use derive({})", trait_name),
+        },
+        Data::Enum(ref data_enum) => (
+            quote!(::core::result::Result<#input_type#ty_generics, &'static str>),
+            enum_content(input_type, data_enum, &method_ident),
+        ),
+
+        _ => panic!("Only structs and enums can use derive({})", trait_name),
+    };
+
+    quote!(
+        impl#impl_generics ::core::ops::#trait_ident for #input_type#ty_generics #where_clause {
+            type Output = #output_type;
+            #[inline]
+            fn #method_ident(self, rhs: #input_type#ty_generics) -> #output_type {
+                #block
+            }
+        }
+    )
+}
+
+fn tuple_content<T: ToTokens>(
+    input_type: &T,
+    fields: &[&Field],
+    method_ident: &Ident,
+) -> TokenStream {
+    let exprs = tuple_exprs(fields, method_ident);
+    quote!(#input_type(#(#exprs),*))
+}
+
+fn struct_content(
+    input_type: &Ident,
+    fields: &[&Field],
+    method_ident: &Ident,
+) -> TokenStream {
+    // It's safe to unwrap because struct fields always have an identifier
+    let exprs = struct_exprs(fields, method_ident);
+    let field_names = field_idents(fields);
+
+    quote!(#input_type{#(#field_names: #exprs),*})
+}
+
+#[allow(clippy::cognitive_complexity)]
+fn enum_content(
+    input_type: &Ident,
+    data_enum: &DataEnum,
+    method_ident: &Ident,
+) -> TokenStream {
+    let mut matches = vec![];
+    let mut method_iter = iter::repeat(method_ident);
+
+    for variant in &data_enum.variants {
+        let subtype = &variant.ident;
+        let subtype = quote!(#input_type::#subtype);
+
+        match variant.fields {
+            Fields::Unnamed(ref fields) => {
+                // The patern that is outputted should look like this:
+                // (Subtype(left_vars), TypePath(right_vars)) => Ok(TypePath(exprs))
+                let size = unnamed_to_vec(fields).len();
+                let l_vars = &numbered_vars(size, "l_");
+                let r_vars = &numbered_vars(size, "r_");
+                let method_iter = method_iter.by_ref();
+                let matcher = quote! {
+                    (#subtype(#(#l_vars),*),
+                     #subtype(#(#r_vars),*)) => {
+                        ::core::result::Result::Ok(#subtype(#(#l_vars.#method_iter(#r_vars)),*))
+                    }
+                };
+                matches.push(matcher);
+            }
+            Fields::Named(ref fields) => {
+                // The patern that is outputted should look like this:
+                // (Subtype{a: __l_a, ...}, Subtype{a: __r_a, ...} => {
+                //     Ok(Subtype{a: __l_a.add(__r_a), ...})
+                // }
+                let field_vec = named_to_vec(fields);
+                let size = field_vec.len();
+                let field_names = &field_idents(&field_vec);
+                let l_vars = &numbered_vars(size, "l_");
+                let r_vars = &numbered_vars(size, "r_");
+                let method_iter = method_iter.by_ref();
+                let matcher = quote! {
+                    (#subtype{#(#field_names: #l_vars),*},
+                     #subtype{#(#field_names: #r_vars),*}) => {
+                        ::core::result::Result::Ok(#subtype{#(#field_names: #l_vars.#method_iter(#r_vars)),*})
+                    }
+                };
+                matches.push(matcher);
+            }
+            Fields::Unit => {
+                let message = format!("Cannot {}() unit variants", method_ident);
+                matches.push(quote!((#subtype, #subtype) => ::core::result::Result::Err(#message)));
+            }
+        }
+    }
+
+    if data_enum.variants.len() > 1 {
+        // In the strange case where there's only one enum variant this is would be an unreachable
+        // match.
+        let message = format!("Trying to {} mismatched enum variants", method_ident);
+        matches.push(quote!(_ => ::core::result::Result::Err(#message)));
+    }
+    quote!(
+        match (self, rhs) {
+            #(#matches),*
+        }
+    )
+}
diff --git a/src/as_mut.rs b/src/as_mut.rs
new file mode 100644 (file)
index 0000000..8394464
--- /dev/null
@@ -0,0 +1,83 @@
+use crate::utils::{
+    add_where_clauses_for_new_ident, AttrParams, MultiFieldData, State,
+};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{parse::Result, DeriveInput, Ident};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let as_mut_type = &Ident::new("__AsMutT", Span::call_site());
+    let state = State::with_type_bound(
+        input,
+        trait_name,
+        quote!(::core::convert),
+        String::from("as_mut"),
+        AttrParams::ignore_and_forward(),
+        false,
+    )?;
+    let MultiFieldData {
+        fields,
+        input_type,
+        members,
+        infos,
+        trait_path,
+        impl_generics,
+        ty_generics,
+        where_clause,
+        ..
+    } = state.enabled_fields_data();
+    let sub_items: Vec<_> = infos
+        .iter()
+        .zip(members.iter())
+        .zip(fields)
+        .map(|((info, member), field)| {
+            let field_type = &field.ty;
+            if info.forward {
+                let trait_path = quote!(#trait_path<#as_mut_type>);
+                let type_where_clauses = quote! {
+                    where #field_type: #trait_path
+                };
+                let new_generics = add_where_clauses_for_new_ident(
+                    &input.generics,
+                    &[field],
+                    as_mut_type,
+                    type_where_clauses,
+                    false,
+                );
+                let (impl_generics, _, where_clause) = new_generics.split_for_impl();
+                let casted_trait = quote!(<#field_type as #trait_path>);
+                (
+                    quote!(#casted_trait::as_mut(&mut #member)),
+                    quote!(#impl_generics),
+                    quote!(#where_clause),
+                    quote!(#trait_path),
+                    quote!(#as_mut_type),
+                )
+            } else {
+                (
+                    quote!(&mut #member),
+                    quote!(#impl_generics),
+                    quote!(#where_clause),
+                    quote!(#trait_path<#field_type>),
+                    quote!(#field_type),
+                )
+            }
+        })
+        .collect();
+    let bodies = sub_items.iter().map(|i| &i.0);
+    let impl_genericses = sub_items.iter().map(|i| &i.1);
+    let where_clauses = sub_items.iter().map(|i| &i.2);
+    let trait_paths = sub_items.iter().map(|i| &i.3);
+    let return_types = sub_items.iter().map(|i| &i.4);
+
+    Ok(quote! {#(
+        impl#impl_genericses #trait_paths for #input_type#ty_generics
+        #where_clauses
+        {
+            #[inline]
+            fn as_mut(&mut self) -> &mut #return_types {
+                #bodies
+            }
+        }
+    )*})
+}
diff --git a/src/as_ref.rs b/src/as_ref.rs
new file mode 100644 (file)
index 0000000..4b8ba85
--- /dev/null
@@ -0,0 +1,83 @@
+use crate::utils::{
+    add_where_clauses_for_new_ident, AttrParams, MultiFieldData, State,
+};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{parse::Result, DeriveInput, Ident};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let as_ref_type = &Ident::new("__AsRefT", Span::call_site());
+    let state = State::with_type_bound(
+        input,
+        trait_name,
+        quote!(::core::convert),
+        String::from("as_ref"),
+        AttrParams::ignore_and_forward(),
+        false,
+    )?;
+    let MultiFieldData {
+        fields,
+        input_type,
+        members,
+        infos,
+        trait_path,
+        impl_generics,
+        ty_generics,
+        where_clause,
+        ..
+    } = state.enabled_fields_data();
+    let sub_items: Vec<_> = infos
+        .iter()
+        .zip(members.iter())
+        .zip(fields)
+        .map(|((info, member), field)| {
+            let field_type = &field.ty;
+            if info.forward {
+                let trait_path = quote!(#trait_path<#as_ref_type>);
+                let type_where_clauses = quote! {
+                    where #field_type: #trait_path
+                };
+                let new_generics = add_where_clauses_for_new_ident(
+                    &input.generics,
+                    &[field],
+                    as_ref_type,
+                    type_where_clauses,
+                    false,
+                );
+                let (impl_generics, _, where_clause) = new_generics.split_for_impl();
+                let casted_trait = quote!(<#field_type as #trait_path>);
+                (
+                    quote!(#casted_trait::as_ref(&#member)),
+                    quote!(#impl_generics),
+                    quote!(#where_clause),
+                    quote!(#trait_path),
+                    quote!(#as_ref_type),
+                )
+            } else {
+                (
+                    quote!(&#member),
+                    quote!(#impl_generics),
+                    quote!(#where_clause),
+                    quote!(#trait_path<#field_type>),
+                    quote!(#field_type),
+                )
+            }
+        })
+        .collect();
+    let bodies = sub_items.iter().map(|i| &i.0);
+    let impl_generics = sub_items.iter().map(|i| &i.1);
+    let where_clauses = sub_items.iter().map(|i| &i.2);
+    let trait_paths = sub_items.iter().map(|i| &i.3);
+    let return_types = sub_items.iter().map(|i| &i.4);
+
+    Ok(quote! {#(
+        impl#impl_generics #trait_paths for #input_type#ty_generics
+        #where_clauses
+        {
+            #[inline]
+            fn as_ref(&self) -> &#return_types {
+                #bodies
+            }
+        }
+    )*})
+}
diff --git a/src/constructor.rs b/src/constructor.rs
new file mode 100644 (file)
index 0000000..14d488b
--- /dev/null
@@ -0,0 +1,49 @@
+use crate::utils::{
+    field_idents, get_field_types, named_to_vec, numbered_vars, unnamed_to_vec,
+};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{Data, DeriveInput, Field, Fields, Ident};
+
+/// Provides the hook to expand `#[derive(Constructor)]` into an implementation of `Constructor`
+pub fn expand(input: &DeriveInput, _: &str) -> TokenStream {
+    let input_type = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+    let ((body, vars), fields) = match input.data {
+        Data::Struct(ref data_struct) => match data_struct.fields {
+            Fields::Unnamed(ref fields) => {
+                let field_vec = unnamed_to_vec(fields);
+                (tuple_body(input_type, &field_vec), field_vec)
+            }
+            Fields::Named(ref fields) => {
+                let field_vec = named_to_vec(fields);
+                (struct_body(input_type, &field_vec), field_vec)
+            }
+            Fields::Unit => (struct_body(input_type, &[]), vec![]),
+        },
+        _ => panic!("Only structs can derive a constructor"),
+    };
+    let original_types = &get_field_types(&fields);
+    quote! {
+        #[allow(missing_docs)]
+        impl#impl_generics #input_type#ty_generics #where_clause {
+            #[inline]
+            pub fn new(#(#vars: #original_types),*) -> #input_type#ty_generics {
+                #body
+            }
+        }
+    }
+}
+
+fn tuple_body(return_type: &Ident, fields: &[&Field]) -> (TokenStream, Vec<Ident>) {
+    let vars = &numbered_vars(fields.len(), "");
+    (quote!(#return_type(#(#vars),*)), vars.clone())
+}
+
+fn struct_body(return_type: &Ident, fields: &[&Field]) -> (TokenStream, Vec<Ident>) {
+    let field_names: &Vec<Ident> =
+        &field_idents(fields).iter().map(|f| (**f).clone()).collect();
+    let vars = field_names;
+    let ret_vars = field_names.clone();
+    (quote!(#return_type{#(#field_names: #vars),*}), ret_vars)
+}
diff --git a/src/deref.rs b/src/deref.rs
new file mode 100644 (file)
index 0000000..60e49a7
--- /dev/null
@@ -0,0 +1,55 @@
+use crate::utils::{add_extra_where_clauses, SingleFieldData, State};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{parse::Result, DeriveInput};
+
+/// Provides the hook to expand `#[derive(Deref)]` into an implementation of `Deref`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_field_ignore_and_forward(
+        input,
+        trait_name,
+        quote!(::core::ops),
+        trait_name.to_lowercase(),
+    )?;
+    let SingleFieldData {
+        input_type,
+        field_type,
+        trait_path,
+        casted_trait,
+        ty_generics,
+        member,
+        info,
+        ..
+    } = state.assert_single_enabled_field();
+
+    let (target, body, generics) = if info.forward {
+        (
+            quote!(#casted_trait::Target),
+            quote!(#casted_trait::deref(&#member)),
+            add_extra_where_clauses(
+                &input.generics,
+                quote! {
+                    where #field_type: #trait_path
+                },
+            ),
+        )
+    } else {
+        (
+            quote!(#field_type),
+            quote!(&#member),
+            input.generics.clone(),
+        )
+    };
+    let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+    Ok(quote! {
+        impl#impl_generics #trait_path for #input_type#ty_generics #where_clause
+        {
+            type Target = #target;
+            #[inline]
+            fn deref(&self) -> &Self::Target {
+                #body
+            }
+        }
+    })
+}
diff --git a/src/deref_mut.rs b/src/deref_mut.rs
new file mode 100644 (file)
index 0000000..85d6512
--- /dev/null
@@ -0,0 +1,48 @@
+use crate::utils::{add_extra_where_clauses, SingleFieldData, State};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{parse::Result, DeriveInput};
+
+/// Provides the hook to expand `#[derive(DerefMut)]` into an implementation of `DerefMut`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_field_ignore_and_forward(
+        input,
+        trait_name,
+        quote!(::core::ops),
+        String::from("deref_mut"),
+    )?;
+    let SingleFieldData {
+        input_type,
+        trait_path,
+        casted_trait,
+        ty_generics,
+        field_type,
+        member,
+        info,
+        ..
+    } = state.assert_single_enabled_field();
+    let (body, generics) = if info.forward {
+        (
+            quote!(#casted_trait::deref_mut(&mut #member)),
+            add_extra_where_clauses(
+                &input.generics,
+                quote! {
+                    where #field_type: #trait_path
+                },
+            ),
+        )
+    } else {
+        (quote!(&mut #member), input.generics.clone())
+    };
+    let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+    Ok(quote! {
+        impl#impl_generics #trait_path for #input_type#ty_generics #where_clause
+        {
+            #[inline]
+            fn deref_mut(&mut self) -> &mut Self::Target {
+                #body
+            }
+        }
+    })
+}
diff --git a/src/display.rs b/src/display.rs
new file mode 100644 (file)
index 0000000..127531d
--- /dev/null
@@ -0,0 +1,888 @@
+use std::{fmt::Display, str::FromStr as _};
+
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{quote, quote_spanned};
+use syn::{
+    parse::Parser as _, punctuated::Punctuated, spanned::Spanned as _, Error, Result,
+};
+
+use crate::utils;
+use utils::{HashMap, HashSet};
+
+/// Provides the hook to expand `#[derive(Display)]` into an implementation of `From`
+pub fn expand(input: &syn::DeriveInput, trait_name: &str) -> Result<TokenStream> {
+    let trait_name = trait_name.trim_end_matches("Custom");
+    let trait_ident = syn::Ident::new(trait_name, Span::call_site());
+    let trait_path = &quote!(::core::fmt::#trait_ident);
+    let trait_attr = trait_name_to_attribute_name(trait_name);
+    let type_params = input
+        .generics
+        .type_params()
+        .map(|t| t.ident.clone())
+        .collect();
+
+    let ParseResult {
+        arms,
+        bounds,
+        requires_helper,
+    } = State {
+        trait_path,
+        trait_attr,
+        input,
+        type_params,
+    }
+    .get_match_arms_and_extra_bounds()?;
+
+    let generics = if !bounds.is_empty() {
+        let bounds: Vec<_> = bounds
+            .into_iter()
+            .map(|(ty, trait_names)| {
+                let bounds: Vec<_> = trait_names
+                    .into_iter()
+                    .map(|bound| quote!(#bound))
+                    .collect();
+                quote!(#ty: #(#bounds)+*)
+            })
+            .collect();
+        let where_clause = quote_spanned!(input.span()=> where #(#bounds),*);
+        utils::add_extra_where_clauses(&input.generics, where_clause)
+    } else {
+        input.generics.clone()
+    };
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+    let name = &input.ident;
+
+    let helper_struct = if requires_helper {
+        display_as_helper_struct()
+    } else {
+        TokenStream::new()
+    };
+
+    Ok(quote! {
+        impl #impl_generics #trait_path for #name #ty_generics #where_clause
+        {
+            #[allow(unused_variables)]
+            #[inline]
+            fn fmt(&self, _derive_more_display_formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+                #helper_struct
+
+                match self {
+                    #arms
+                    _ => Ok(()) // This is needed for empty enums
+                }
+            }
+        }
+    })
+}
+
+fn trait_name_to_attribute_name(trait_name: &str) -> &'static str {
+    match trait_name {
+        "Display" => "display",
+        "Binary" => "binary",
+        "Octal" => "octal",
+        "LowerHex" => "lower_hex",
+        "UpperHex" => "upper_hex",
+        "LowerExp" => "lower_exp",
+        "UpperExp" => "upper_exp",
+        "Pointer" => "pointer",
+        "Debug" => "debug",
+        _ => unimplemented!(),
+    }
+}
+
+fn attribute_name_to_trait_name(attribute_name: &str) -> &'static str {
+    match attribute_name {
+        "display" => "Display",
+        "binary" => "Binary",
+        "octal" => "Octal",
+        "lower_hex" => "LowerHex",
+        "upper_hex" => "UpperHex",
+        "lower_exp" => "LowerExp",
+        "upper_exp" => "UpperExp",
+        "pointer" => "Pointer",
+        _ => unreachable!(),
+    }
+}
+
+fn trait_name_to_trait_bound(trait_name: &str) -> syn::TraitBound {
+    let path_segments_iterator = vec!["core", "fmt", trait_name]
+        .into_iter()
+        .map(|segment| syn::PathSegment::from(Ident::new(segment, Span::call_site())));
+
+    syn::TraitBound {
+        lifetimes: None,
+        modifier: syn::TraitBoundModifier::None,
+        paren_token: None,
+        path: syn::Path {
+            leading_colon: Some(syn::Token![::](Span::call_site())),
+            segments: path_segments_iterator.collect(),
+        },
+    }
+}
+
+/// Create a helper struct that is required by some `Display` impls.
+///
+/// The struct is necessary in cases where `Display` is derived for an enum
+/// with an outer `#[display(fmt = "...")]` attribute and if that outer
+/// format-string contains a single placeholder. In that case, we have to
+/// format twice:
+///
+/// - we need to format each variant according to its own, optional
+///   format-string,
+/// - we then need to insert this formatted variant into the outer
+///   format-string.
+///
+/// This helper struct solves this as follows:
+/// - formatting the whole object inserts the helper struct into the outer
+///   format string,
+/// - upon being formatted, the helper struct calls an inner closure to produce
+///   its formatted result,
+/// - the closure in turn uses the inner, optional format-string to produce its
+///   result. If there is no inner format-string, it falls back to plain
+///   `$trait::fmt()`.
+fn display_as_helper_struct() -> TokenStream {
+    quote! {
+        struct _derive_more_DisplayAs<F>(F)
+        where
+            F: ::core::ops::Fn(&mut ::core::fmt::Formatter) -> ::core::fmt::Result;
+
+        const _derive_more_DisplayAs_impl: () = {
+            impl<F> ::core::fmt::Display for _derive_more_DisplayAs<F>
+            where
+                F: ::core::ops::Fn(&mut ::core::fmt::Formatter) -> ::core::fmt::Result
+            {
+                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+                    (self.0)(f)
+                }
+            }
+        };
+    }
+}
+
+/// Result type of `State::get_match_arms_and_extra_bounds()`.
+#[derive(Default)]
+struct ParseResult {
+    /// The match arms destructuring `self`.
+    arms: TokenStream,
+    /// Any trait bounds that may be required.
+    bounds: HashMap<syn::Type, HashSet<syn::TraitBound>>,
+    /// `true` if the Display impl requires the `DisplayAs` helper struct.
+    requires_helper: bool,
+}
+
+struct State<'a, 'b> {
+    trait_path: &'b TokenStream,
+    trait_attr: &'static str,
+    input: &'a syn::DeriveInput,
+    type_params: HashSet<Ident>,
+}
+
+impl<'a, 'b> State<'a, 'b> {
+    fn get_proper_fmt_syntax(&self) -> impl Display {
+        format!(
+            r#"Proper syntax: #[{}(fmt = "My format", "arg1", "arg2")]"#,
+            self.trait_attr
+        )
+    }
+    fn get_proper_bound_syntax(&self) -> impl Display {
+        format!(
+            "Proper syntax: #[{}(bound = \"T, U: Trait1 + Trait2, V: Trait3\")]",
+            self.trait_attr
+        )
+    }
+
+    fn get_matcher(&self, fields: &syn::Fields) -> TokenStream {
+        match fields {
+            syn::Fields::Unit => TokenStream::new(),
+            syn::Fields::Unnamed(fields) => {
+                let fields: TokenStream = (0..fields.unnamed.len())
+                    .map(|n| {
+                        let i = Ident::new(&format!("_{}", n), Span::call_site());
+                        quote!(#i,)
+                    })
+                    .collect();
+                quote!((#fields))
+            }
+            syn::Fields::Named(fields) => {
+                let fields: TokenStream = fields
+                    .named
+                    .iter()
+                    .map(|f| {
+                        let i = f.ident.as_ref().unwrap();
+                        quote!(#i,)
+                    })
+                    .collect();
+                quote!({#fields})
+            }
+        }
+    }
+    fn find_meta(
+        &self,
+        attrs: &[syn::Attribute],
+        meta_key: &str,
+    ) -> Result<Option<syn::Meta>> {
+        let mut metas = Vec::new();
+        for meta in attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
+            let meta_list = match &meta {
+                syn::Meta::List(meta) => meta,
+                _ => continue,
+            };
+
+            if !meta_list.path.is_ident(self.trait_attr) {
+                continue;
+            }
+
+            use syn::{Meta, NestedMeta};
+            let meta_nv = match meta_list.nested.first() {
+                Some(NestedMeta::Meta(Meta::NameValue(meta_nv))) => meta_nv,
+                _ => {
+                    // If the given attribute is not MetaNameValue, it most likely implies that the
+                    // user is writing an incorrect format. For example:
+                    // - `#[display()]`
+                    // - `#[display("foo")]`
+                    // - `#[display(foo)]`
+                    return Err(Error::new(
+                        meta.span(),
+                        format!(
+                            r#"The format for this attribute cannot be parsed. Correct format: `#[{}({} = "...")]`"#,
+                            self.trait_attr, meta_key
+                        ),
+                    ));
+                }
+            };
+
+            if meta_nv.path.is_ident(meta_key) {
+                metas.push(meta);
+            }
+        }
+
+        let mut iter = metas.into_iter();
+        let meta = iter.next();
+        if iter.next().is_none() {
+            Ok(meta)
+        } else {
+            Err(Error::new(meta.span(), "Too many attributes specified"))
+        }
+    }
+    fn parse_meta_bounds(
+        &self,
+        bounds: &syn::LitStr,
+    ) -> Result<HashMap<syn::Type, HashSet<syn::TraitBound>>> {
+        let span = bounds.span();
+
+        let input = bounds.value();
+        let tokens = TokenStream::from_str(&input)?;
+        let parser = Punctuated::<syn::GenericParam, syn::Token![,]>::parse_terminated;
+
+        let generic_params = parser
+            .parse2(tokens)
+            .map_err(|error| Error::new(span, error.to_string()))?;
+
+        if generic_params.is_empty() {
+            return Err(Error::new(span, "No bounds specified"));
+        }
+
+        let mut bounds = HashMap::default();
+
+        for generic_param in generic_params {
+            let type_param = match generic_param {
+                syn::GenericParam::Type(type_param) => type_param,
+                _ => return Err(Error::new(span, "Only trait bounds allowed")),
+            };
+
+            if !self.type_params.contains(&type_param.ident) {
+                return Err(Error::new(
+                    span,
+                    "Unknown generic type argument specified",
+                ));
+            } else if !type_param.attrs.is_empty() {
+                return Err(Error::new(span, "Attributes aren't allowed"));
+            } else if type_param.eq_token.is_some() || type_param.default.is_some() {
+                return Err(Error::new(span, "Default type parameters aren't allowed"));
+            }
+
+            let ident = type_param.ident.to_string();
+
+            let ty = syn::Type::Path(syn::TypePath {
+                qself: None,
+                path: type_param.ident.into(),
+            });
+            let bounds = bounds.entry(ty).or_insert_with(HashSet::default);
+
+            for bound in type_param.bounds {
+                let bound = match bound {
+                    syn::TypeParamBound::Trait(bound) => bound,
+                    _ => return Err(Error::new(span, "Only trait bounds allowed")),
+                };
+
+                if bound.lifetimes.is_some() {
+                    return Err(Error::new(
+                        span,
+                        "Higher-rank trait bounds aren't allowed",
+                    ));
+                }
+
+                bounds.insert(bound);
+            }
+
+            if bounds.is_empty() {
+                return Err(Error::new(
+                    span,
+                    format!("No bounds specified for type parameter {}", ident),
+                ));
+            }
+        }
+
+        Ok(bounds)
+    }
+    fn parse_meta_fmt(
+        &self,
+        meta: &syn::Meta,
+        outer_enum: bool,
+    ) -> Result<(TokenStream, bool)> {
+        let list = match meta {
+            syn::Meta::List(list) => list,
+            _ => {
+                return Err(Error::new(meta.span(), self.get_proper_fmt_syntax()));
+            }
+        };
+
+        match &list.nested[0] {
+            syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
+                path,
+                lit: syn::Lit::Str(fmt),
+                ..
+            })) => match path {
+                op if op.segments.first().expect("path shouldn't be empty").ident
+                    == "fmt" =>
+                {
+                    let expected_affix_usage = "outer `enum` `fmt` is an affix spec that expects no args and at most 1 placeholder for inner variant display";
+                    if outer_enum {
+                        if list.nested.iter().skip(1).count() != 0 {
+                            return Err(Error::new(
+                                list.nested[1].span(),
+                                expected_affix_usage,
+                            ));
+                        }
+                        // TODO: Check for a single `Display` group?
+                        let fmt_string = match &list.nested[0] {
+                            syn::NestedMeta::Meta(syn::Meta::NameValue(
+                                syn::MetaNameValue {
+                                    path,
+                                    lit: syn::Lit::Str(s),
+                                    ..
+                                },
+                            )) if path
+                                .segments
+                                .first()
+                                .expect("path shouldn't be empty")
+                                .ident
+                                == "fmt" =>
+                            {
+                                s.value()
+                            }
+                            // This one has been checked already in get_meta_fmt() method.
+                            _ => unreachable!(),
+                        };
+
+                        let num_placeholders =
+                            Placeholder::parse_fmt_string(&fmt_string).len();
+                        if num_placeholders > 1 {
+                            return Err(Error::new(
+                                list.nested[1].span(),
+                                expected_affix_usage,
+                            ));
+                        }
+                        if num_placeholders == 1 {
+                            return Ok((quote_spanned!(fmt.span()=> #fmt), true));
+                        }
+                    }
+                    let args = list
+                        .nested
+                        .iter()
+                        .skip(1) // skip fmt = "..."
+                        .try_fold(TokenStream::new(), |args, arg| {
+                            let arg = match arg {
+                                syn::NestedMeta::Lit(syn::Lit::Str(s)) => s,
+                                syn::NestedMeta::Meta(syn::Meta::Path(i)) => {
+                                    return Ok(quote_spanned!(list.span()=> #args #i,));
+                                }
+                                _ => {
+                                    return Err(Error::new(
+                                        arg.span(),
+                                        self.get_proper_fmt_syntax(),
+                                    ))
+                                }
+                            };
+                            let arg: TokenStream =
+                                arg.parse().map_err(|e| Error::new(arg.span(), e))?;
+                            Ok(quote_spanned!(list.span()=> #args #arg,))
+                        })?;
+
+                    Ok((
+                        quote_spanned!(meta.span()=> write!(_derive_more_display_formatter, #fmt, #args)),
+                        false,
+                    ))
+                }
+                _ => Err(Error::new(
+                    list.nested[0].span(),
+                    self.get_proper_fmt_syntax(),
+                )),
+            },
+            _ => Err(Error::new(
+                list.nested[0].span(),
+                self.get_proper_fmt_syntax(),
+            )),
+        }
+    }
+    fn infer_fmt(&self, fields: &syn::Fields, name: &Ident) -> Result<TokenStream> {
+        let fields = match fields {
+            syn::Fields::Unit => {
+                return Ok(quote!(
+                    _derive_more_display_formatter.write_str(stringify!(#name))
+                ))
+            }
+            syn::Fields::Named(fields) => &fields.named,
+            syn::Fields::Unnamed(fields) => &fields.unnamed,
+        };
+        if fields.is_empty() {
+            return Ok(quote!(
+                _derive_more_display_formatter.write_str(stringify!(#name))
+            ));
+        } else if fields.len() > 1 {
+            return Err(Error::new(
+                fields.span(),
+                "Cannot automatically infer format for types with more than 1 field",
+            ));
+        }
+
+        let trait_path = self.trait_path;
+        if let Some(ident) = &fields.iter().next().as_ref().unwrap().ident {
+            Ok(quote!(#trait_path::fmt(#ident, _derive_more_display_formatter)))
+        } else {
+            Ok(quote!(#trait_path::fmt(_0, _derive_more_display_formatter)))
+        }
+    }
+    fn get_match_arms_and_extra_bounds(&self) -> Result<ParseResult> {
+        let result: Result<_> = match &self.input.data {
+            syn::Data::Enum(e) => {
+                match self
+                    .find_meta(&self.input.attrs, "fmt")
+                    .and_then(|m| m.map(|m| self.parse_meta_fmt(&m, true)).transpose())?
+                {
+                    // #[display(fmt = "no placeholder")] on whole enum.
+                    Some((fmt, false)) => {
+                        e.variants.iter().try_for_each(|v| {
+                            if let Some(meta) = self.find_meta(&v.attrs, "fmt")? {
+                                Err(Error::new(
+                                    meta.span(),
+                                    "`fmt` cannot be used on variant when the whole enum has a format string without a placeholder, maybe you want to add a placeholder?",
+                                ))
+                            } else {
+                                Ok(())
+                            }
+                        })?;
+
+                        Ok(ParseResult {
+                            arms: quote_spanned!(self.input.span()=> _ => #fmt,),
+                            bounds: HashMap::default(),
+                            requires_helper: false,
+                        })
+                    }
+                    // #[display(fmt = "one placeholder: {}")] on whole enum.
+                    Some((outer_fmt, true)) => {
+                        let fmt: Result<TokenStream> = e.variants.iter().try_fold(TokenStream::new(), |arms, v| {
+                            let matcher = self.get_matcher(&v.fields);
+                            let fmt = if let Some(meta) = self.find_meta(&v.attrs, "fmt")? {
+                                self.parse_meta_fmt(&meta, false)?.0
+                            } else {
+                                self.infer_fmt(&v.fields, &v.ident)?
+                            };
+                            let name = &self.input.ident;
+                            let v_name = &v.ident;
+                            Ok(quote_spanned!(fmt.span()=> #arms #name::#v_name #matcher => write!(
+                                _derive_more_display_formatter,
+                                #outer_fmt,
+                                _derive_more_DisplayAs(|_derive_more_display_formatter| #fmt)
+                            ),))
+                        });
+                        let fmt = fmt?;
+                        Ok(ParseResult {
+                            arms: quote_spanned!(self.input.span()=> #fmt),
+                            bounds: HashMap::default(),
+                            requires_helper: true,
+                        })
+                    }
+                    // No format attribute on whole enum.
+                    None => e.variants.iter().try_fold(ParseResult::default(), |result, v| {
+                        let ParseResult{ arms, mut bounds, requires_helper } = result;
+                        let matcher = self.get_matcher(&v.fields);
+                        let name = &self.input.ident;
+                        let v_name = &v.ident;
+                        let fmt: TokenStream;
+                        let these_bounds: HashMap<_, _>;
+
+                        if let Some(meta) = self.find_meta(&v.attrs, "fmt")? {
+                            fmt = self.parse_meta_fmt(&meta, false)?.0;
+                            these_bounds = self.get_used_type_params_bounds(&v.fields, &meta);
+                        } else {
+                            fmt = self.infer_fmt(&v.fields, v_name)?;
+                            these_bounds = self.infer_type_params_bounds(&v.fields);
+                        };
+                        these_bounds.into_iter().for_each(|(ty, trait_names)| {
+                            bounds.entry(ty).or_default().extend(trait_names)
+                        });
+                        let arms = quote_spanned!(self.input.span()=> #arms #name::#v_name #matcher => #fmt,);
+
+                        Ok(ParseResult{ arms, bounds, requires_helper })
+                    }),
+                }
+            }
+            syn::Data::Struct(s) => {
+                let matcher = self.get_matcher(&s.fields);
+                let name = &self.input.ident;
+                let fmt: TokenStream;
+                let bounds: HashMap<_, _>;
+
+                if let Some(meta) = self.find_meta(&self.input.attrs, "fmt")? {
+                    fmt = self.parse_meta_fmt(&meta, false)?.0;
+                    bounds = self.get_used_type_params_bounds(&s.fields, &meta);
+                } else {
+                    fmt = self.infer_fmt(&s.fields, name)?;
+                    bounds = self.infer_type_params_bounds(&s.fields);
+                }
+
+                Ok(ParseResult {
+                    arms: quote_spanned!(self.input.span()=> #name #matcher => #fmt,),
+                    bounds,
+                    requires_helper: false,
+                })
+            }
+            syn::Data::Union(_) => {
+                let meta =
+                    self.find_meta(&self.input.attrs, "fmt")?.ok_or_else(|| {
+                        Error::new(
+                            self.input.span(),
+                            "Cannot automatically infer format for unions",
+                        )
+                    })?;
+                let fmt = self.parse_meta_fmt(&meta, false)?.0;
+
+                Ok(ParseResult {
+                    arms: quote_spanned!(self.input.span()=> _ => #fmt,),
+                    bounds: HashMap::default(),
+                    requires_helper: false,
+                })
+            }
+        };
+
+        let mut result = result?;
+
+        let meta = match self.find_meta(&self.input.attrs, "bound")? {
+            Some(meta) => meta,
+            _ => return Ok(result),
+        };
+
+        let span = meta.span();
+
+        let meta = match meta {
+            syn::Meta::List(meta) => meta.nested,
+            _ => return Err(Error::new(span, self.get_proper_bound_syntax())),
+        };
+
+        if meta.len() != 1 {
+            return Err(Error::new(span, self.get_proper_bound_syntax()));
+        }
+
+        let meta = match &meta[0] {
+            syn::NestedMeta::Meta(syn::Meta::NameValue(meta)) => meta,
+            _ => return Err(Error::new(span, self.get_proper_bound_syntax())),
+        };
+
+        let extra_bounds = match &meta.lit {
+            syn::Lit::Str(extra_bounds) => extra_bounds,
+            _ => return Err(Error::new(span, self.get_proper_bound_syntax())),
+        };
+
+        let extra_bounds = self.parse_meta_bounds(extra_bounds)?;
+
+        extra_bounds.into_iter().for_each(|(ty, trait_names)| {
+            result.bounds.entry(ty).or_default().extend(trait_names)
+        });
+
+        Ok(result)
+    }
+    fn get_used_type_params_bounds(
+        &self,
+        fields: &syn::Fields,
+        meta: &syn::Meta,
+    ) -> HashMap<syn::Type, HashSet<syn::TraitBound>> {
+        if self.type_params.is_empty() {
+            return HashMap::default();
+        }
+
+        let fields_type_params: HashMap<syn::Path, _> = fields
+            .iter()
+            .enumerate()
+            .filter_map(|(i, field)| {
+                utils::get_if_type_parameter_used_in_type(&self.type_params, &field.ty)
+                    .map(|ty| {
+                        (
+                            field
+                                .ident
+                                .clone()
+                                .unwrap_or_else(|| {
+                                    Ident::new(&format!("_{}", i), Span::call_site())
+                                })
+                                .into(),
+                            ty,
+                        )
+                    })
+            })
+            .collect();
+        if fields_type_params.is_empty() {
+            return HashMap::default();
+        }
+
+        let list = match meta {
+            syn::Meta::List(list) => list,
+            // This one has been checked already in get_meta_fmt() method.
+            _ => unreachable!(),
+        };
+        let fmt_args: HashMap<_, _> = list
+            .nested
+            .iter()
+            .skip(1) // skip fmt = "..."
+            .enumerate()
+            .filter_map(|(i, arg)| match arg {
+                syn::NestedMeta::Lit(syn::Lit::Str(ref s)) => {
+                    syn::parse_str(&s.value()).ok().map(|id| (i, id))
+                }
+                syn::NestedMeta::Meta(syn::Meta::Path(ref id)) => Some((i, id.clone())),
+                // This one has been checked already in get_meta_fmt() method.
+                _ => unreachable!(),
+            })
+            .collect();
+        if fmt_args.is_empty() {
+            return HashMap::default();
+        }
+        let fmt_string = match &list.nested[0] {
+            syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
+                path,
+                lit: syn::Lit::Str(s),
+                ..
+            })) if path
+                .segments
+                .first()
+                .expect("path shouldn't be empty")
+                .ident
+                == "fmt" =>
+            {
+                s.value()
+            }
+            // This one has been checked already in get_meta_fmt() method.
+            _ => unreachable!(),
+        };
+
+        Placeholder::parse_fmt_string(&fmt_string).into_iter().fold(
+            HashMap::default(),
+            |mut bounds, pl| {
+                if let Some(arg) = fmt_args.get(&pl.position) {
+                    if fields_type_params.contains_key(arg) {
+                        bounds
+                            .entry(fields_type_params[arg].clone())
+                            .or_insert_with(HashSet::default)
+                            .insert(trait_name_to_trait_bound(pl.trait_name));
+                    }
+                }
+                bounds
+            },
+        )
+    }
+    fn infer_type_params_bounds(
+        &self,
+        fields: &syn::Fields,
+    ) -> HashMap<syn::Type, HashSet<syn::TraitBound>> {
+        if self.type_params.is_empty() {
+            return HashMap::default();
+        }
+        if let syn::Fields::Unit = fields {
+            return HashMap::default();
+        }
+        // infer_fmt() uses only first field.
+        fields
+            .iter()
+            .take(1)
+            .filter_map(|field| {
+                utils::get_if_type_parameter_used_in_type(&self.type_params, &field.ty)
+                    .map(|ty| {
+                        (
+                            ty,
+                            [trait_name_to_trait_bound(attribute_name_to_trait_name(
+                                self.trait_attr,
+                            ))]
+                            .iter()
+                            .cloned()
+                            .collect(),
+                        )
+                    })
+            })
+            .collect()
+    }
+}
+
+/// Representation of formatting placeholder.
+#[derive(Debug, PartialEq)]
+struct Placeholder {
+    /// Position of formatting argument to be used for this placeholder.
+    position: usize,
+    /// Name of [`std::fmt`] trait to be used for rendering this placeholder.
+    trait_name: &'static str,
+}
+
+impl Placeholder {
+    /// Parses [`Placeholder`]s from a given formatting string.
+    fn parse_fmt_string(s: &str) -> Vec<Placeholder> {
+        let mut n = 0;
+        crate::parsing::all_placeholders(s)
+            .into_iter()
+            .flatten()
+            .map(|m| {
+                let (maybe_arg, maybe_typ) = crate::parsing::format(m).unwrap();
+                let position = maybe_arg.unwrap_or_else(|| {
+                    // Assign "the next argument".
+                    // https://doc.rust-lang.org/stable/std/fmt/index.html#positional-parameters
+                    n += 1;
+                    n - 1
+                });
+                let typ = maybe_typ.unwrap_or_default();
+                let trait_name = match typ {
+                    "" => "Display",
+                    "?" | "x?" | "X?" => "Debug",
+                    "o" => "Octal",
+                    "x" => "LowerHex",
+                    "X" => "UpperHex",
+                    "p" => "Pointer",
+                    "b" => "Binary",
+                    "e" => "LowerExp",
+                    "E" => "UpperExp",
+                    _ => unreachable!(),
+                };
+                Placeholder {
+                    position,
+                    trait_name,
+                }
+            })
+            .collect()
+    }
+}
+
+#[cfg(test)]
+mod regex_maybe_placeholder_spec {
+
+    #[test]
+    fn parses_placeholders_and_omits_escaped() {
+        let fmt_string = "{}, {:?}, {{}}, {{{1:0$}}}";
+        let placeholders: Vec<_> = crate::parsing::all_placeholders(&fmt_string)
+            .into_iter()
+            .flatten()
+            .collect();
+        assert_eq!(placeholders, vec!["{}", "{:?}", "{1:0$}"]);
+    }
+}
+
+#[cfg(test)]
+mod regex_placeholder_format_spec {
+
+    #[test]
+    fn detects_type() {
+        for (p, expected) in vec![
+            ("{}", ""),
+            ("{:?}", "?"),
+            ("{:x?}", "x?"),
+            ("{:X?}", "X?"),
+            ("{:o}", "o"),
+            ("{:x}", "x"),
+            ("{:X}", "X"),
+            ("{:p}", "p"),
+            ("{:b}", "b"),
+            ("{:e}", "e"),
+            ("{:E}", "E"),
+            ("{:.*}", ""),
+            ("{8}", ""),
+            ("{:04}", ""),
+            ("{1:0$}", ""),
+            ("{:width$}", ""),
+            ("{9:>8.*}", ""),
+            ("{2:.1$x}", "x"),
+        ] {
+            let typ = crate::parsing::format(p).unwrap().1.unwrap_or_default();
+            assert_eq!(typ, expected);
+        }
+    }
+
+    #[test]
+    fn detects_arg() {
+        for (p, expected) in vec![
+            ("{}", ""),
+            ("{0:?}", "0"),
+            ("{12:x?}", "12"),
+            ("{3:X?}", "3"),
+            ("{5:o}", "5"),
+            ("{6:x}", "6"),
+            ("{:X}", ""),
+            ("{8}", "8"),
+            ("{:04}", ""),
+            ("{1:0$}", "1"),
+            ("{:width$}", ""),
+            ("{9:>8.*}", "9"),
+            ("{2:.1$x}", "2"),
+        ] {
+            let arg = crate::parsing::format(p)
+                .unwrap()
+                .0
+                .map(|s| s.to_string())
+                .unwrap_or_default();
+            assert_eq!(arg, String::from(expected));
+        }
+    }
+}
+
+#[cfg(test)]
+mod placeholder_parse_fmt_string_spec {
+    use super::*;
+
+    #[test]
+    fn indicates_position_and_trait_name_for_each_fmt_placeholder() {
+        let fmt_string = "{},{:?},{{}},{{{1:0$}}}-{2:.1$x}{0:#?}{:width$}";
+        assert_eq!(
+            Placeholder::parse_fmt_string(&fmt_string),
+            vec![
+                Placeholder {
+                    position: 0,
+                    trait_name: "Display",
+                },
+                Placeholder {
+                    position: 1,
+                    trait_name: "Debug",
+                },
+                Placeholder {
+                    position: 1,
+                    trait_name: "Display",
+                },
+                Placeholder {
+                    position: 2,
+                    trait_name: "LowerHex",
+                },
+                Placeholder {
+                    position: 0,
+                    trait_name: "Debug",
+                },
+                Placeholder {
+                    position: 2,
+                    trait_name: "Display",
+                },
+            ],
+        )
+    }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644 (file)
index 0000000..5767290
--- /dev/null
@@ -0,0 +1,439 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{spanned::Spanned as _, Error, Result};
+
+use crate::utils::{
+    self, AttrParams, DeriveType, FullMetaInfo, HashSet, MetaInfo, MultiFieldData,
+    State,
+};
+
+pub fn expand(
+    input: &syn::DeriveInput,
+    trait_name: &'static str,
+) -> Result<TokenStream> {
+    let syn::DeriveInput {
+        ident, generics, ..
+    } = input;
+
+    let state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(::std::error),
+        trait_name.to_lowercase(),
+        allowed_attr_params(),
+    )?;
+
+    let type_params: HashSet<_> = generics
+        .params
+        .iter()
+        .filter_map(|generic| match generic {
+            syn::GenericParam::Type(ty) => Some(ty.ident.clone()),
+            _ => None,
+        })
+        .collect();
+
+    let (bounds, source, backtrace) = match state.derive_type {
+        DeriveType::Named | DeriveType::Unnamed => render_struct(&type_params, &state)?,
+        DeriveType::Enum => render_enum(&type_params, &state)?,
+    };
+
+    let source = source.map(|source| {
+        quote! {
+            fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
+                #source
+            }
+        }
+    });
+
+    let backtrace = backtrace.map(|backtrace| {
+        quote! {
+            fn backtrace(&self) -> Option<&::std::backtrace::Backtrace> {
+                #backtrace
+            }
+        }
+    });
+
+    let mut generics = generics.clone();
+
+    if !type_params.is_empty() {
+        let generic_parameters = generics.params.iter();
+        generics = utils::add_extra_where_clauses(
+            &generics,
+            quote! {
+                where
+                    #ident<#(#generic_parameters),*>: ::std::fmt::Debug + ::std::fmt::Display
+            },
+        );
+    }
+
+    if !bounds.is_empty() {
+        let bounds = bounds.iter();
+        generics = utils::add_extra_where_clauses(
+            &generics,
+            quote! {
+                where
+                    #(#bounds: ::std::fmt::Debug + ::std::fmt::Display + ::std::error::Error + 'static),*
+            },
+        );
+    }
+
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let render = quote! {
+        impl#impl_generics ::std::error::Error for #ident#ty_generics #where_clause {
+            #source
+            #backtrace
+        }
+    };
+
+    Ok(render)
+}
+
+fn render_struct(
+    type_params: &HashSet<syn::Ident>,
+    state: &State,
+) -> Result<(HashSet<syn::Type>, Option<TokenStream>, Option<TokenStream>)> {
+    let parsed_fields = parse_fields(type_params, state)?;
+
+    let source = parsed_fields.render_source_as_struct();
+    let backtrace = parsed_fields.render_backtrace_as_struct();
+
+    Ok((parsed_fields.bounds, source, backtrace))
+}
+
+fn render_enum(
+    type_params: &HashSet<syn::Ident>,
+    state: &State,
+) -> Result<(HashSet<syn::Type>, Option<TokenStream>, Option<TokenStream>)> {
+    let mut bounds = HashSet::default();
+    let mut source_match_arms = Vec::new();
+    let mut backtrace_match_arms = Vec::new();
+
+    for variant in state.enabled_variant_data().variants {
+        let default_info = FullMetaInfo {
+            enabled: true,
+            ..FullMetaInfo::default()
+        };
+
+        let state = State::from_variant(
+            state.input,
+            state.trait_name,
+            state.trait_module.clone(),
+            state.trait_attr.clone(),
+            allowed_attr_params(),
+            variant,
+            default_info,
+        )?;
+
+        let parsed_fields = parse_fields(type_params, &state)?;
+
+        if let Some(expr) = parsed_fields.render_source_as_enum_variant_match_arm() {
+            source_match_arms.push(expr);
+        }
+
+        if let Some(expr) = parsed_fields.render_backtrace_as_enum_variant_match_arm() {
+            backtrace_match_arms.push(expr);
+        }
+
+        bounds.extend(parsed_fields.bounds.into_iter());
+    }
+
+    let render = |match_arms: &mut Vec<TokenStream>| {
+        if !match_arms.is_empty() && match_arms.len() < state.variants.len() {
+            match_arms.push(quote!(_ => None));
+        }
+
+        if !match_arms.is_empty() {
+            let expr = quote! {
+                match self {
+                    #(#match_arms),*
+                }
+            };
+
+            Some(expr)
+        } else {
+            None
+        }
+    };
+
+    let source = render(&mut source_match_arms);
+    let backtrace = render(&mut backtrace_match_arms);
+
+    Ok((bounds, source, backtrace))
+}
+
+fn allowed_attr_params() -> AttrParams {
+    AttrParams {
+        enum_: vec!["ignore"],
+        struct_: vec!["ignore"],
+        variant: vec!["ignore"],
+        field: vec!["ignore", "source", "backtrace"],
+    }
+}
+
+struct ParsedFields<'input, 'state> {
+    data: MultiFieldData<'input, 'state>,
+    source: Option<usize>,
+    backtrace: Option<usize>,
+    bounds: HashSet<syn::Type>,
+}
+
+impl<'input, 'state> ParsedFields<'input, 'state> {
+    fn new(data: MultiFieldData<'input, 'state>) -> Self {
+        Self {
+            data,
+            source: None,
+            backtrace: None,
+            bounds: HashSet::default(),
+        }
+    }
+}
+
+impl<'input, 'state> ParsedFields<'input, 'state> {
+    fn render_source_as_struct(&self) -> Option<TokenStream> {
+        let source = self.source?;
+        let ident = &self.data.members[source];
+        Some(render_some(quote!(&#ident)))
+    }
+
+    fn render_source_as_enum_variant_match_arm(&self) -> Option<TokenStream> {
+        let source = self.source?;
+        let pattern = self.data.matcher(&[source], &[quote!(source)]);
+        let expr = render_some(quote!(source));
+        Some(quote!(#pattern => #expr))
+    }
+
+    fn render_backtrace_as_struct(&self) -> Option<TokenStream> {
+        let backtrace = self.backtrace?;
+        let backtrace_expr = &self.data.members[backtrace];
+        Some(quote!(Some(&#backtrace_expr)))
+    }
+
+    fn render_backtrace_as_enum_variant_match_arm(&self) -> Option<TokenStream> {
+        let backtrace = self.backtrace?;
+        let pattern = self.data.matcher(&[backtrace], &[quote!(backtrace)]);
+        Some(quote!(#pattern => Some(backtrace)))
+    }
+}
+
+fn render_some<T>(expr: T) -> TokenStream
+where
+    T: quote::ToTokens,
+{
+    quote!(Some(#expr as &(dyn ::std::error::Error + 'static)))
+}
+
+fn parse_fields<'input, 'state>(
+    type_params: &HashSet<syn::Ident>,
+    state: &'state State<'input>,
+) -> Result<ParsedFields<'input, 'state>> {
+    let mut parsed_fields = match state.derive_type {
+        DeriveType::Named => {
+            parse_fields_impl(state, |attr, field, _| {
+                // Unwrapping is safe, cause fields in named struct
+                // always have an ident
+                let ident = field.ident.as_ref().unwrap();
+
+                match attr {
+                    "source" => ident == "source",
+                    "backtrace" => {
+                        ident == "backtrace"
+                            || is_type_path_ends_with_segment(&field.ty, "Backtrace")
+                    }
+                    _ => unreachable!(),
+                }
+            })
+        }
+
+        DeriveType::Unnamed => {
+            let mut parsed_fields =
+                parse_fields_impl(state, |attr, field, len| match attr {
+                    "source" => {
+                        len == 1
+                            && !is_type_path_ends_with_segment(&field.ty, "Backtrace")
+                    }
+                    "backtrace" => {
+                        is_type_path_ends_with_segment(&field.ty, "Backtrace")
+                    }
+                    _ => unreachable!(),
+                })?;
+
+            parsed_fields.source = parsed_fields
+                .source
+                .or_else(|| infer_source_field(&state.fields, &parsed_fields));
+
+            Ok(parsed_fields)
+        }
+
+        _ => unreachable!(),
+    }?;
+
+    if let Some(source) = parsed_fields.source {
+        add_bound_if_type_parameter_used_in_type(
+            &mut parsed_fields.bounds,
+            type_params,
+            &state.fields[source].ty,
+        );
+    }
+
+    Ok(parsed_fields)
+}
+
+/// Checks if `ty` is [`syn::Type::Path`] and ends with segment matching `tail`
+/// and doesn't contain any generic parameters.
+fn is_type_path_ends_with_segment(ty: &syn::Type, tail: &str) -> bool {
+    let ty = match ty {
+        syn::Type::Path(ty) => ty,
+        _ => return false,
+    };
+
+    // Unwrapping is safe, cause 'syn::TypePath.path.segments'
+    // have to have at least one segment
+    let segment = ty.path.segments.last().unwrap();
+
+    match segment.arguments {
+        syn::PathArguments::None => (),
+        _ => return false,
+    };
+
+    segment.ident == tail
+}
+
+fn infer_source_field(
+    fields: &[&syn::Field],
+    parsed_fields: &ParsedFields,
+) -> Option<usize> {
+    // if we have exactly two fields
+    if fields.len() != 2 {
+        return None;
+    }
+
+    // no source field was specified/inferred
+    if parsed_fields.source.is_some() {
+        return None;
+    }
+
+    // but one of the fields was specified/inferred as backtrace field
+    if let Some(backtrace) = parsed_fields.backtrace {
+        // then infer *other field* as source field
+        let source = (backtrace + 1) % 2;
+        // unless it was explicitly marked as non-source
+        if parsed_fields.data.infos[source].info.source != Some(false) {
+            return Some(source);
+        }
+    }
+
+    None
+}
+
+fn parse_fields_impl<'input, 'state, P>(
+    state: &'state State<'input>,
+    is_valid_default_field_for_attr: P,
+) -> Result<ParsedFields<'input, 'state>>
+where
+    P: Fn(&str, &syn::Field, usize) -> bool,
+{
+    let MultiFieldData { fields, infos, .. } = state.enabled_fields_data();
+
+    let iter = fields
+        .iter()
+        .zip(infos.iter().map(|info| &info.info))
+        .enumerate()
+        .map(|(index, (field, info))| (index, *field, info));
+
+    let source = parse_field_impl(
+        &is_valid_default_field_for_attr,
+        state.fields.len(),
+        iter.clone(),
+        "source",
+        |info| info.source,
+    )?;
+
+    let backtrace = parse_field_impl(
+        &is_valid_default_field_for_attr,
+        state.fields.len(),
+        iter.clone(),
+        "backtrace",
+        |info| info.backtrace,
+    )?;
+
+    let mut parsed_fields = ParsedFields::new(state.enabled_fields_data());
+
+    if let Some((index, _, _)) = source {
+        parsed_fields.source = Some(index);
+    }
+
+    if let Some((index, _, _)) = backtrace {
+        parsed_fields.backtrace = Some(index);
+    }
+
+    Ok(parsed_fields)
+}
+
+fn parse_field_impl<'a, P, V>(
+    is_valid_default_field_for_attr: &P,
+    len: usize,
+    iter: impl Iterator<Item = (usize, &'a syn::Field, &'a MetaInfo)> + Clone,
+    attr: &str,
+    value: V,
+) -> Result<Option<(usize, &'a syn::Field, &'a MetaInfo)>>
+where
+    P: Fn(&str, &syn::Field, usize) -> bool,
+    V: Fn(&MetaInfo) -> Option<bool>,
+{
+    let explicit_fields = iter.clone().filter(|(_, _, info)| match value(info) {
+        Some(true) => true,
+        _ => false,
+    });
+
+    let inferred_fields = iter.filter(|(_, field, info)| match value(info) {
+        None => is_valid_default_field_for_attr(attr, field, len),
+        _ => false,
+    });
+
+    let field = assert_iter_contains_zero_or_one_item(
+        explicit_fields,
+        &format!(
+            "Multiple `{}` attributes specified. \
+             Single attribute per struct/enum variant allowed.",
+            attr
+        ),
+    )?;
+
+    let field = match field {
+        field @ Some(_) => field,
+        None => assert_iter_contains_zero_or_one_item(
+            inferred_fields,
+            "Conflicting fields found. Consider specifying some \
+             `#[error(...)]` attributes to resolve conflict.",
+        )?,
+    };
+
+    Ok(field)
+}
+
+fn assert_iter_contains_zero_or_one_item<'a>(
+    mut iter: impl Iterator<Item = (usize, &'a syn::Field, &'a MetaInfo)>,
+    error_msg: &str,
+) -> Result<Option<(usize, &'a syn::Field, &'a MetaInfo)>> {
+    let item = match iter.next() {
+        Some(item) => item,
+        None => return Ok(None),
+    };
+
+    if let Some((_, field, _)) = iter.next() {
+        return Err(Error::new(field.span(), error_msg));
+    }
+
+    Ok(Some(item))
+}
+
+fn add_bound_if_type_parameter_used_in_type(
+    bounds: &mut HashSet<syn::Type>,
+    type_params: &HashSet<syn::Ident>,
+    ty: &syn::Type,
+) {
+    if let Some(ty) = utils::get_if_type_parameter_used_in_type(type_params, ty) {
+        bounds.insert(ty);
+    }
+}
diff --git a/src/from.rs b/src/from.rs
new file mode 100644 (file)
index 0000000..56ff5bc
--- /dev/null
@@ -0,0 +1,142 @@
+use std::iter;
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, ToTokens};
+use syn::{parse::Result, DeriveInput, Ident, Index};
+
+use crate::utils::{
+    add_where_clauses_for_new_ident, AttrParams, DeriveType, HashMap, MultiFieldData,
+    RefType, State,
+};
+
+/// Provides the hook to expand `#[derive(From)]` into an implementation of `From`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(::core::convert),
+        trait_name.to_lowercase(),
+        AttrParams {
+            enum_: vec!["forward", "ignore"],
+            variant: vec!["forward", "ignore", "types"],
+            struct_: vec!["forward", "types"],
+            field: vec!["forward"],
+        },
+    )?;
+    if state.derive_type == DeriveType::Enum {
+        Ok(enum_from(input, state))
+    } else {
+        Ok(struct_from(input, &state))
+    }
+}
+
+pub fn struct_from(input: &DeriveInput, state: &State) -> TokenStream {
+    let multi_field_data = state.enabled_fields_data();
+    let MultiFieldData {
+        fields,
+        variant_info,
+        infos,
+        input_type,
+        trait_path,
+        ..
+    } = multi_field_data.clone();
+
+    let additional_types = variant_info.additional_types(RefType::No);
+    let mut impls = Vec::with_capacity(additional_types.len() + 1);
+    for explicit_type in iter::once(None).chain(additional_types.iter().map(Some)) {
+        let mut new_generics = input.generics.clone();
+
+        let mut initializers = Vec::with_capacity(infos.len());
+        let mut from_types = Vec::with_capacity(infos.len());
+        for (i, (info, field)) in infos.iter().zip(fields.iter()).enumerate() {
+            let field_type = &field.ty;
+            let variable = if fields.len() == 1 {
+                quote! { original }
+            } else {
+                let tuple_index = Index::from(i);
+                quote! { original.#tuple_index }
+            };
+            if let Some(type_) = explicit_type {
+                initializers.push(quote! {
+                    <#field_type as #trait_path<#type_>>::from(#variable)
+                });
+                from_types.push(quote! { #type_ });
+            } else if info.forward {
+                let type_param =
+                    &Ident::new(&format!("__FromT{}", i), Span::call_site());
+                let sub_trait_path = quote! { #trait_path<#type_param> };
+                let type_where_clauses = quote! {
+                    where #field_type: #sub_trait_path
+                };
+                new_generics = add_where_clauses_for_new_ident(
+                    &new_generics,
+                    &[field],
+                    type_param,
+                    type_where_clauses,
+                    true,
+                );
+                let casted_trait = quote! { <#field_type as #sub_trait_path> };
+                initializers.push(quote! { #casted_trait::from(#variable) });
+                from_types.push(quote! { #type_param });
+            } else {
+                initializers.push(variable);
+                from_types.push(quote! { #field_type });
+            }
+        }
+
+        let body = multi_field_data.initializer(&initializers);
+        let (impl_generics, _, where_clause) = new_generics.split_for_impl();
+        let (_, ty_generics, _) = input.generics.split_for_impl();
+
+        impls.push(quote! {
+            #[automatically_derived]
+            impl#impl_generics #trait_path<(#(#from_types),*)> for
+                #input_type#ty_generics #where_clause {
+
+                #[inline]
+                fn from(original: (#(#from_types),*)) -> #input_type#ty_generics {
+                    #body
+                }
+            }
+        });
+    }
+
+    quote! { #( #impls )* }
+}
+
+fn enum_from(input: &DeriveInput, state: State) -> TokenStream {
+    let mut tokens = TokenStream::new();
+
+    let mut variants_per_types = HashMap::default();
+    for variant_state in state.enabled_variant_data().variant_states {
+        let multi_field_data = variant_state.enabled_fields_data();
+        let MultiFieldData { field_types, .. } = multi_field_data.clone();
+        variants_per_types
+            .entry(field_types.clone())
+            .or_insert_with(Vec::new)
+            .push(variant_state);
+    }
+    for (ref field_types, ref variant_states) in variants_per_types {
+        for variant_state in variant_states {
+            let multi_field_data = variant_state.enabled_fields_data();
+            let MultiFieldData {
+                variant_info,
+                infos,
+                ..
+            } = multi_field_data.clone();
+            // If there would be a conflict on a empty tuple derive, ignore the
+            // variants that are not explicitly enabled or have explicitly enabled
+            // or disabled fields
+            if field_types.is_empty()
+                && variant_states.len() > 1
+                && !std::iter::once(variant_info)
+                    .chain(infos)
+                    .any(|info| info.info.enabled.is_some())
+            {
+                continue;
+            }
+            struct_from(input, variant_state).to_tokens(&mut tokens);
+        }
+    }
+    tokens
+}
diff --git a/src/from_str.rs b/src/from_str.rs
new file mode 100644 (file)
index 0000000..5f26fdd
--- /dev/null
@@ -0,0 +1,49 @@
+use crate::utils::{SingleFieldData, State};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{parse::Result, DeriveInput};
+
+/// Provides the hook to expand `#[derive(FromStr)]` into an implementation of `FromStr`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::new(
+        input,
+        trait_name,
+        quote!(::core::str),
+        trait_name.to_lowercase(),
+    )?;
+
+    // We cannot set defaults for fields, once we do we can remove this check
+    if state.fields.len() != 1 || state.enabled_fields().len() != 1 {
+        panic_one_field(trait_name);
+    }
+
+    let single_field_data = state.assert_single_enabled_field();
+    let SingleFieldData {
+        input_type,
+        field_type,
+        trait_path,
+        casted_trait,
+        impl_generics,
+        ty_generics,
+        where_clause,
+        ..
+    } = single_field_data.clone();
+
+    let initializers = [quote!(#casted_trait::from_str(src)?)];
+    let body = single_field_data.initializer(&initializers);
+
+    Ok(quote! {
+        impl#impl_generics #trait_path for #input_type#ty_generics #where_clause
+        {
+            type Err = <#field_type as #trait_path>::Err;
+            #[inline]
+            fn from_str(src: &str) -> ::core::result::Result<Self, Self::Err> {
+                Ok(#body)
+            }
+        }
+    })
+}
+
+fn panic_one_field(trait_name: &str) -> ! {
+    panic!("Only structs with one field can derive({})", trait_name)
+}
diff --git a/src/index.rs b/src/index.rs
new file mode 100644 (file)
index 0000000..5aea597
--- /dev/null
@@ -0,0 +1,50 @@
+use crate::utils::{add_where_clauses_for_new_ident, SingleFieldData, State};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{parse::Result, DeriveInput, Ident};
+
+/// Provides the hook to expand `#[derive(Index)]` into an implementation of `Index`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let index_type = &Ident::new("__IdxT", Span::call_site());
+    let mut state = State::with_field_ignore(
+        input,
+        trait_name,
+        quote!(::core::ops),
+        trait_name.to_lowercase(),
+    )?;
+    state.add_trait_path_type_param(quote!(#index_type));
+    let SingleFieldData {
+        field,
+        field_type,
+        input_type,
+        trait_path_with_params,
+        casted_trait,
+        member,
+        ..
+    } = state.assert_single_enabled_field();
+
+    let type_where_clauses = quote! {
+        where #field_type: #trait_path_with_params
+    };
+
+    let new_generics = add_where_clauses_for_new_ident(
+        &input.generics,
+        &[field],
+        index_type,
+        type_where_clauses,
+        true,
+    );
+
+    let (impl_generics, _, where_clause) = new_generics.split_for_impl();
+    let (_, ty_generics, _) = input.generics.split_for_impl();
+    Ok(quote! {
+        impl#impl_generics #trait_path_with_params for #input_type#ty_generics #where_clause
+        {
+            type Output = #casted_trait::Output;
+            #[inline]
+            fn index(&self, idx: #index_type) -> &Self::Output {
+                #casted_trait::index(&#member, idx)
+            }
+        }
+    })
+}
diff --git a/src/index_mut.rs b/src/index_mut.rs
new file mode 100644 (file)
index 0000000..d2c3688
--- /dev/null
@@ -0,0 +1,49 @@
+use crate::utils::{add_where_clauses_for_new_ident, SingleFieldData, State};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{parse::Result, DeriveInput, Ident};
+
+/// Provides the hook to expand `#[derive(IndexMut)]` into an implementation of `IndexMut`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let index_type = &Ident::new("__IdxT", Span::call_site());
+    let mut state = State::with_field_ignore(
+        input,
+        trait_name,
+        quote!(::core::ops),
+        String::from("index_mut"),
+    )?;
+    state.add_trait_path_type_param(quote!(#index_type));
+    let SingleFieldData {
+        field,
+        field_type,
+        input_type,
+        trait_path_with_params,
+        casted_trait,
+        member,
+        ..
+    } = state.assert_single_enabled_field();
+
+    let type_where_clauses = quote! {
+        where #field_type: #trait_path_with_params
+    };
+
+    let new_generics = add_where_clauses_for_new_ident(
+        &input.generics,
+        &[field],
+        index_type,
+        type_where_clauses,
+        true,
+    );
+
+    let (impl_generics, _, where_clause) = new_generics.split_for_impl();
+    let (_, ty_generics, _) = input.generics.split_for_impl();
+    Ok(quote! {
+        impl#impl_generics #trait_path_with_params for #input_type#ty_generics #where_clause
+        {
+            #[inline]
+            fn index_mut(&mut self, idx: #index_type) -> &mut Self::Output {
+                #casted_trait::index_mut(&mut #member, idx)
+            }
+        }
+    })
+}
diff --git a/src/into.rs b/src/into.rs
new file mode 100644 (file)
index 0000000..61d820e
--- /dev/null
@@ -0,0 +1,83 @@
+use std::iter;
+
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{parse::Result, DeriveInput};
+
+use crate::utils::{add_extra_generic_param, AttrParams, MultiFieldData, State};
+
+/// Provides the hook to expand `#[derive(Into)]` into an implementation of `Into`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(::core::convert),
+        trait_name.to_lowercase(),
+        AttrParams {
+            enum_: vec!["ignore", "owned", "ref", "ref_mut"],
+            variant: vec!["ignore", "owned", "ref", "ref_mut"],
+            struct_: vec!["ignore", "owned", "ref", "ref_mut", "types"],
+            field: vec!["ignore"],
+        },
+    )?;
+    let MultiFieldData {
+        variant_info,
+        field_types,
+        field_idents,
+        input_type,
+        ..
+    } = state.enabled_fields_data();
+
+    let mut tokens = TokenStream::new();
+
+    for ref_type in variant_info.ref_types() {
+        let reference = ref_type.reference();
+        let lifetime = ref_type.lifetime();
+        let reference_with_lifetime = ref_type.reference_with_lifetime();
+
+        let generics_impl;
+        let (_, ty_generics, where_clause) = input.generics.split_for_impl();
+        let (impl_generics, _, _) = if ref_type.is_ref() {
+            generics_impl = add_extra_generic_param(&input.generics, lifetime);
+            generics_impl.split_for_impl()
+        } else {
+            input.generics.split_for_impl()
+        };
+
+        let additional_types = variant_info.additional_types(ref_type);
+        for explicit_type in iter::once(None).chain(additional_types.iter().map(Some)) {
+            let into_types: Vec<_> = field_types
+                .iter()
+                .map(|field_type| {
+                    // No, `.unwrap_or()` won't work here, because we use different types.
+                    if let Some(type_) = explicit_type {
+                        quote! { #reference_with_lifetime #type_ }
+                    } else {
+                        quote! { #reference_with_lifetime #field_type }
+                    }
+                })
+                .collect();
+
+            let initializers = field_idents.iter().map(|field_ident| {
+                if let Some(type_) = explicit_type {
+                    quote! { <#reference #type_>::from(#reference original.#field_ident) }
+                } else {
+                    quote! { #reference original.#field_ident }
+                }
+            });
+
+            (quote! {
+                #[automatically_derived]
+                impl#impl_generics ::core::convert::From<#reference_with_lifetime #input_type#ty_generics> for
+                    (#(#into_types),*) #where_clause {
+
+                    #[inline]
+                    fn from(original: #reference_with_lifetime #input_type#ty_generics) -> Self {
+                        (#(#initializers),*)
+                    }
+                }
+            }).to_tokens(&mut tokens);
+        }
+    }
+    Ok(tokens)
+}
diff --git a/src/into_iterator.rs b/src/into_iterator.rs
new file mode 100644 (file)
index 0000000..45183d4
--- /dev/null
@@ -0,0 +1,59 @@
+use crate::utils::{
+    add_extra_generic_param, add_extra_ty_param_bound_ref, SingleFieldData, State,
+};
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{parse::Result, DeriveInput};
+
+/// Provides the hook to expand `#[derive(IntoIterator)]` into an implementation of `IntoIterator`
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_field_ignore_and_refs(
+        input,
+        trait_name,
+        quote!(::core::iter),
+        String::from("into_iterator"),
+    )?;
+    let SingleFieldData {
+        input_type,
+        info,
+        field_type,
+        member,
+        trait_path,
+        ..
+    } = state.assert_single_enabled_field();
+
+    let mut tokens = TokenStream::new();
+
+    for ref_type in info.ref_types() {
+        let reference = ref_type.reference();
+        let lifetime = ref_type.lifetime();
+        let reference_with_lifetime = ref_type.reference_with_lifetime();
+
+        let generics_impl;
+        let generics =
+            add_extra_ty_param_bound_ref(&input.generics, trait_path, ref_type);
+        let (_, ty_generics, where_clause) = generics.split_for_impl();
+        let (impl_generics, _, _) = if ref_type.is_ref() {
+            generics_impl = add_extra_generic_param(&generics, lifetime.clone());
+            generics_impl.split_for_impl()
+        } else {
+            generics.split_for_impl()
+        };
+        // let generics = add_extra_ty_param_bound(&input.generics, trait_path);
+        let casted_trait =
+            &quote!(<#reference_with_lifetime #field_type as #trait_path>);
+        let into_iterator = quote! {
+            impl#impl_generics #trait_path for #reference_with_lifetime #input_type#ty_generics #where_clause
+            {
+                type Item = #casted_trait::Item;
+                type IntoIter = #casted_trait::IntoIter;
+                #[inline]
+                fn into_iter(self) -> Self::IntoIter {
+                    #casted_trait::into_iter(#reference #member)
+                }
+            }
+        };
+        into_iterator.to_tokens(&mut tokens);
+    }
+    Ok(tokens)
+}
diff --git a/src/is_variant.rs b/src/is_variant.rs
new file mode 100644 (file)
index 0000000..3f19298
--- /dev/null
@@ -0,0 +1,62 @@
+use crate::utils::{AttrParams, DeriveType, State};
+use convert_case::{Case, Casing};
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use syn::{DeriveInput, Fields, Ident, Result};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(),
+        String::from("is_variant"),
+        AttrParams {
+            enum_: vec!["ignore"],
+            variant: vec!["ignore"],
+            struct_: vec!["ignore"],
+            field: vec!["ignore"],
+        },
+    )?;
+    assert!(
+        state.derive_type == DeriveType::Enum,
+        "IsVariant can only be derived for enums"
+    );
+
+    let enum_name = &input.ident;
+    let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl();
+
+    let mut funcs = vec![];
+    for variant_state in state.enabled_variant_data().variant_states {
+        let variant = variant_state.variant.unwrap();
+        let fn_name = Ident::new(
+            &format_ident!("is_{}", variant.ident)
+                .to_string()
+                .to_case(Case::Snake),
+            variant.ident.span(),
+        );
+        let variant_ident = &variant.ident;
+
+        let data_pattern = match variant.fields {
+            Fields::Named(_) => quote! { {..} },
+            Fields::Unnamed(_) => quote! { (..) },
+            Fields::Unit => quote! {},
+        };
+        let func = quote! {
+            pub fn #fn_name(&self) -> bool {
+                match self {
+                    #enum_name ::#variant_ident #data_pattern => true,
+                    _ => false
+                }
+            }
+        };
+        funcs.push(func);
+    }
+
+    let imp = quote! {
+        impl #imp_generics #enum_name #type_generics #where_clause{
+            #(#funcs)*
+        }
+    };
+
+    Ok(imp)
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..339e750
--- /dev/null
@@ -0,0 +1,422 @@
+//! # `derive_more`
+//!
+//! [![Build Status](https://github.com/JelteF/derive_more/workflows/CI/badge.svg)](https://github.com/JelteF/derive_more/actions)
+//! [![Latest Version](https://img.shields.io/crates/v/derive_more.svg)](https://crates.io/crates/derive_more)
+//! [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://jeltef.github.io/derive_more/derive_more/)
+//! [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/JelteF/derive_more/master/LICENSE)
+//! [![Rust 1.36+](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html)
+//!
+//! Rust has lots of builtin traits that are implemented for its basic types, such
+//! as `Add`, `Not`, `From` or `Display`.
+//! However, when wrapping these types inside your own structs or enums you lose the
+//! implementations of these traits and are required to recreate them.
+//! This is especially annoying when your own structures are very simple, such as
+//! when using the commonly advised newtype pattern (e.g. `MyInt(i32)`).
+//!
+//! This library tries to remove these annoyances and the corresponding boilerplate code.
+//! It does this by allowing you to derive lots of commonly used traits for both structs and enums.
+//!
+//! ## Example code
+//!
+//! By using this library the following code just works:
+//!
+//! ```rust
+//! extern crate derive_more;
+//! use derive_more::{Add, Display, From, Into};
+//!
+//! #[derive(PartialEq, From, Add)]
+//! struct MyInt(i32);
+//!
+//! #[derive(PartialEq, From, Into)]
+//! struct Point2D {
+//!     x: i32,
+//!     y: i32,
+//! }
+//!
+//! #[derive(PartialEq, From, Add, Display)]
+//! enum MyEnum {
+//!     #[display(fmt = "int: {}", _0)]
+//!     Int(i32),
+//!     Uint(u32),
+//!     #[display(fmt = "nothing")]
+//!     Nothing,
+//! }
+//!
+//! assert!(MyInt(11) == MyInt(5) + 6.into());
+//! assert!((5, 6) == Point2D { x: 5, y: 6 }.into());
+//! assert!(MyEnum::Int(15) == (MyEnum::Int(8) + 7.into()).unwrap());
+//! assert!(MyEnum::Int(15).to_string() == "int: 15");
+//! assert!(MyEnum::Uint(42).to_string() == "42");
+//! assert!(MyEnum::Nothing.to_string() == "nothing");
+//! ```
+//!
+//! ## The derivable traits
+//!
+//! Below are all the traits that you can derive using this library.
+//! Some trait derivations are so similar that the further documentation will only show a single one
+//! of them.
+//! You can recognize these by the "-like" suffix in their name.
+//! The trait name before that will be the only one that is used throughout the further
+//! documentation.
+//!
+//! It is important to understand what code gets generated when using one of the
+//! derives from this crate.
+//! That is why the links below explain what code gets generated for a trait for
+//! each group from before.
+//!
+//! You can use the [`cargo-expand`] utility to see the exact code that is generated
+//! for your specific type.
+//! This will show you your code with all macros and derives expanded.
+//!
+//! **NOTE**: You still have to derive each trait separately. So `#[derive(Mul)]` doesn't
+//! automatically derive `Div` as well. To derive both you should do `#[derive(Mul, Div)]`
+//!
+//! ### Conversion traits
+//!
+//! These are traits that are used to convert automatically between types.
+//!
+//! 1. [`From`]
+//! 2. [`Into`]
+//! 3. [`FromStr`]
+//! 4. [`TryInto`]
+//! 5. [`IntoIterator`]
+//! 6. [`AsRef`]
+//! 7. [`AsMut`]
+//!
+//! ### Formatting traits
+//!
+//! These traits are used for converting a struct to a string in different ways.
+//!
+//! 1. [`Display`-like], contains `Display`, `Binary`, `Octal`, `LowerHex`,
+//!    `UpperHex`, `LowerExp`, `UpperExp`, `Pointer`
+//!
+//! ### Error-handling traits
+//! These traits are used to define error-types.
+//!
+//! 1. [`Error`]
+//!
+//! ### Operators
+//!
+//! These are traits that can be used for operator overloading.
+//!
+//! 1. [`Index`]
+//! 2. [`Deref`]
+//! 3. [`Not`-like], contains `Not` and `Neg`
+//! 4. [`Add`-like], contains `Add`, `Sub`, `BitAnd`, `BitOr`, `BitXor`
+//! 5. [`Mul`-like], contains `Mul`, `Div`, `Rem`, `Shr` and `Shl`
+//! 3. [`Sum`-like], contains `Sum` and `Product`
+//! 6. [`IndexMut`]
+//! 7. [`DerefMut`]
+//! 8. [`AddAssign`-like], contains `AddAssign`, `SubAssign`, `BitAndAssign`,
+//!    `BitOrAssign` and `BitXorAssign`
+//! 9. [`MulAssign`-like], contains `MulAssign`, `DivAssign`, `RemAssign`,
+//!    `ShrAssign` and `ShlAssign`
+//!
+//! ### Static methods
+//!
+//! These don't derive traits, but derive static methods instead.
+//!
+//! 1. [`Constructor`], this derives a `new` method that can be used as a constructor.
+//!    This is very basic if you need more customization for your constructor, check
+//!    out the [`derive-new`] crate.
+//! 2. [`IsVariant`], for each variant `foo` of an enum type, derives a `is_foo` method.
+//! 3. [`Unwrap`], for each variant `foo` of an enum type, derives an `unwrap_foo` method.
+//!
+//! ## Generated code
+//!
+//! ## Installation
+//!
+//! This library requires Rust 1.36 or higher and it supports `no_std` out of the box.
+//! Then add the following to `Cargo.toml`:
+//!
+//! ```toml
+//! [dependencies]
+//! derive_more = "0.99.0"
+//! # You can specifiy the types of derives that you need for less time spent
+//! # compiling. For the full list of features see this crate its Cargo.toml.
+//! default-features = false
+//! features = ["from", "add", "iterator"]
+//! ```
+//!
+//! And this to the top of your Rust file for Rust 2018:
+//!
+//! ```rust
+//! extern crate derive_more;
+//! // use the derives that you want in the file
+//! use derive_more::{Add, Display, From};
+//! ```
+//! If you're still using Rust 2015 you should add this instead:
+//! ```rust
+//! extern crate core;
+//! #[macro_use]
+//! extern crate derive_more;
+//! # fn main(){}
+//! ```
+//!
+//! [`cargo-expand`]: https://github.com/dtolnay/cargo-expand
+//! [`derive-new`]: https://github.com/nrc/derive-new
+//!
+//! [`From`]: https://jeltef.github.io/derive_more/derive_more/from.html
+//! [`Into`]: https://jeltef.github.io/derive_more/derive_more/into.html
+//! [`FromStr`]: https://jeltef.github.io/derive_more/derive_more/from_str.html
+//! [`TryInto`]: https://jeltef.github.io/derive_more/derive_more/try_into.html
+//! [`IntoIterator`]: https://jeltef.github.io/derive_more/derive_more/into_iterator.html
+//! [`AsRef`]: https://jeltef.github.io/derive_more/derive_more/as_ref.html
+//! [`AsMut`]: https://jeltef.github.io/derive_more/derive_more/as_mut.html
+//!
+//! [`Display`-like]: https://jeltef.github.io/derive_more/derive_more/display.html
+//!
+//! [`Error`]: https://jeltef.github.io/derive_more/derive_more/error.html
+//!
+//! [`Index`]: https://jeltef.github.io/derive_more/derive_more/index_op.html
+//! [`Deref`]: https://jeltef.github.io/derive_more/derive_more/deref.html
+//! [`Not`-like]: https://jeltef.github.io/derive_more/derive_more/not.html
+//! [`Add`-like]: https://jeltef.github.io/derive_more/derive_more/add.html
+//! [`Mul`-like]: https://jeltef.github.io/derive_more/derive_more/mul.html
+//! [`Sum`-like]: https://jeltef.github.io/derive_more/derive_more/sum.html
+//! [`IndexMut`]: https://jeltef.github.io/derive_more/derive_more/index_mut.html
+//! [`DerefMut`]: https://jeltef.github.io/derive_more/derive_more/deref_mut.html
+//! [`AddAssign`-like]: https://jeltef.github.io/derive_more/derive_more/add_assign.html
+//! [`MulAssign`-like]: https://jeltef.github.io/derive_more/derive_more/mul_assign.html
+//!
+//! [`Constructor`]: https://jeltef.github.io/derive_more/derive_more/constructor.html
+//! [`IsVariant`]: https://jeltef.github.io/derive_more/derive_more/is_variant.html
+//! [`Unwrap`]: https://jeltef.github.io/derive_more/derive_more/unwrap.html
+
+#![recursion_limit = "128"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use syn::parse::Error as ParseError;
+
+mod utils;
+
+#[cfg(any(feature = "add_assign", feature = "mul_assign"))]
+mod add_assign_like;
+#[cfg(any(
+    feature = "add",
+    feature = "add_assign",
+    feature = "mul",
+    feature = "mul_assign",
+))]
+mod add_helpers;
+#[cfg(any(feature = "add", feature = "mul"))]
+mod add_like;
+#[cfg(feature = "as_mut")]
+mod as_mut;
+#[cfg(feature = "as_ref")]
+mod as_ref;
+#[cfg(feature = "constructor")]
+mod constructor;
+#[cfg(feature = "deref")]
+mod deref;
+#[cfg(feature = "deref_mut")]
+mod deref_mut;
+#[cfg(feature = "display")]
+mod display;
+#[cfg(feature = "error")]
+mod error;
+#[cfg(feature = "from")]
+mod from;
+#[cfg(feature = "from_str")]
+mod from_str;
+#[cfg(feature = "index")]
+mod index;
+#[cfg(feature = "index_mut")]
+mod index_mut;
+#[cfg(feature = "into")]
+mod into;
+#[cfg(feature = "into_iterator")]
+mod into_iterator;
+#[cfg(feature = "is_variant")]
+mod is_variant;
+#[cfg(feature = "mul_assign")]
+mod mul_assign_like;
+#[cfg(any(feature = "mul", feature = "mul_assign"))]
+mod mul_helpers;
+#[cfg(feature = "mul")]
+mod mul_like;
+#[cfg(feature = "not")]
+mod not_like;
+#[cfg(feature = "display")]
+#[allow(ellipsis_inclusive_range_patterns)]
+#[allow(clippy::all)]
+mod parsing;
+#[cfg(feature = "sum")]
+mod sum_like;
+#[cfg(feature = "try_into")]
+mod try_into;
+#[cfg(feature = "unwrap")]
+mod unwrap;
+
+// This trait describes the possible return types of
+// the derives. A derive can generally be infallible and
+// return a TokenStream, or it can be fallible and return
+// a Result<TokenStream, syn::parse::Error>.
+trait Output {
+    fn process(self) -> TokenStream;
+}
+
+impl Output for proc_macro2::TokenStream {
+    fn process(self) -> TokenStream {
+        self.into()
+    }
+}
+
+impl Output for Result<proc_macro2::TokenStream, ParseError> {
+    fn process(self) -> TokenStream {
+        match self {
+            Ok(ts) => ts.into(),
+            Err(e) => e.to_compile_error().into(),
+        }
+    }
+}
+
+macro_rules! create_derive(
+    ($feature:literal, $mod_:ident, $trait_:ident, $fn_name: ident $(,$attribute:ident)* $(,)?) => {
+        #[cfg(feature = $feature)]
+        #[proc_macro_derive($trait_, attributes($($attribute),*))]
+        #[doc(hidden)]
+        pub fn $fn_name(input: TokenStream) -> TokenStream {
+            let ast = syn::parse(input).unwrap();
+            Output::process($mod_::expand(&ast, stringify!($trait_)))
+        }
+    }
+);
+
+create_derive!("from", from, From, from_derive, from);
+
+create_derive!("into", into, Into, into_derive, into);
+
+create_derive!("constructor", constructor, Constructor, constructor_derive);
+
+create_derive!("not", not_like, Not, not_derive);
+create_derive!("not", not_like, Neg, neg_derive);
+
+create_derive!("add", add_like, Add, add_derive);
+create_derive!("add", add_like, Sub, sub_derive);
+create_derive!("add", add_like, BitAnd, bit_and_derive);
+create_derive!("add", add_like, BitOr, bit_or_derive);
+create_derive!("add", add_like, BitXor, bit_xor_derive);
+
+create_derive!("mul", mul_like, Mul, mul_derive, mul);
+create_derive!("mul", mul_like, Div, div_derive, div);
+create_derive!("mul", mul_like, Rem, rem_derive, rem);
+create_derive!("mul", mul_like, Shr, shr_derive, shr);
+create_derive!("mul", mul_like, Shl, shl_derive, shl);
+
+create_derive!("add_assign", add_assign_like, AddAssign, add_assign_derive,);
+create_derive!("add_assign", add_assign_like, SubAssign, sub_assign_derive,);
+create_derive!(
+    "add_assign",
+    add_assign_like,
+    BitAndAssign,
+    bit_and_assign_derive,
+);
+create_derive!(
+    "add_assign",
+    add_assign_like,
+    BitOrAssign,
+    bit_or_assign_derive,
+);
+create_derive!(
+    "add_assign",
+    add_assign_like,
+    BitXorAssign,
+    bit_xor_assign_derive,
+);
+
+create_derive!(
+    "mul_assign",
+    mul_assign_like,
+    MulAssign,
+    mul_assign_derive,
+    mul_assign,
+);
+create_derive!(
+    "mul_assign",
+    mul_assign_like,
+    DivAssign,
+    div_assign_derive,
+    div_assign,
+);
+create_derive!(
+    "mul_assign",
+    mul_assign_like,
+    RemAssign,
+    rem_assign_derive,
+    rem_assign,
+);
+create_derive!(
+    "mul_assign",
+    mul_assign_like,
+    ShrAssign,
+    shr_assign_derive,
+    shr_assign,
+);
+create_derive!(
+    "mul_assign",
+    mul_assign_like,
+    ShlAssign,
+    shl_assign_derive,
+    shl_assign,
+);
+
+create_derive!("sum", sum_like, Sum, sum_derive);
+create_derive!("sum", sum_like, Product, product_derive);
+
+create_derive!("error", error, Error, error_derive, error);
+
+create_derive!("from_str", from_str, FromStr, from_str_derive);
+
+create_derive!("display", display, Display, display_derive, display);
+create_derive!("display", display, Binary, binary_derive, binary);
+create_derive!("display", display, Octal, octal_derive, octal);
+create_derive!("display", display, LowerHex, lower_hex_derive, lower_hex);
+create_derive!("display", display, UpperHex, upper_hex_derive, upper_hex);
+create_derive!("display", display, LowerExp, lower_exp_derive, lower_exp);
+create_derive!("display", display, UpperExp, upper_exp_derive, upper_exp);
+create_derive!("display", display, Pointer, pointer_derive, pointer);
+create_derive!("display", display, DebugCustom, debug_custom_derive, debug);
+
+create_derive!("index", index, Index, index_derive, index);
+create_derive!(
+    "index_mut",
+    index_mut,
+    IndexMut,
+    index_mut_derive,
+    index_mut,
+);
+
+create_derive!(
+    "into_iterator",
+    into_iterator,
+    IntoIterator,
+    into_iterator_derive,
+    into_iterator,
+);
+
+create_derive!("try_into", try_into, TryInto, try_into_derive, try_into);
+
+create_derive!("deref", deref, Deref, deref_derive, deref);
+create_derive!(
+    "deref_mut",
+    deref_mut,
+    DerefMut,
+    deref_mut_derive,
+    deref_mut,
+);
+
+create_derive!("as_ref", as_ref, AsRef, as_ref_derive, as_ref);
+create_derive!("as_mut", as_mut, AsMut, as_mut_derive, as_mut);
+
+create_derive!(
+    "is_variant",
+    is_variant,
+    IsVariant,
+    is_variant_derive,
+    is_variant
+);
+
+create_derive!("unwrap", unwrap, Unwrap, unwrap_derive, unwrap);
diff --git a/src/mul_assign_like.rs b/src/mul_assign_like.rs
new file mode 100644 (file)
index 0000000..eaba70d
--- /dev/null
@@ -0,0 +1,65 @@
+use crate::add_assign_like;
+use crate::mul_helpers::generics_and_exprs;
+use crate::utils::{AttrParams, MultiFieldData, RefType, State};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use std::collections::HashSet;
+use std::iter;
+use syn::{DeriveInput, Ident, Result};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let method_name = trait_name
+        .to_lowercase()
+        .trim_end_matches("assign")
+        .to_string()
+        + "_assign";
+
+    let mut state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(::core::ops),
+        method_name,
+        AttrParams::struct_(vec!["forward"]),
+    )?;
+    if state.default_info.forward {
+        return Ok(add_assign_like::expand(input, trait_name));
+    }
+    let scalar_ident = &Ident::new("__RhsT", Span::call_site());
+    state.add_trait_path_type_param(quote!(#scalar_ident));
+    let multi_field_data = state.enabled_fields_data();
+    let MultiFieldData {
+        input_type,
+        field_types,
+        ty_generics,
+        trait_path,
+        trait_path_with_params,
+        method_ident,
+        ..
+    } = multi_field_data.clone();
+
+    let tys = field_types.iter().collect::<HashSet<_>>();
+    let tys = tys.iter();
+    let trait_path_iter = iter::repeat(trait_path_with_params);
+
+    let type_where_clauses = quote! {
+        where #(#tys: #trait_path_iter),*
+    };
+
+    let (generics, exprs) = generics_and_exprs(
+        multi_field_data.clone(),
+        scalar_ident,
+        type_where_clauses,
+        RefType::Mut,
+    );
+    let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+    Ok(quote!(
+        impl#impl_generics #trait_path<#scalar_ident> for #input_type#ty_generics #where_clause{
+            #[inline]
+            fn #method_ident(&mut self, rhs: #scalar_ident) {
+                #(#exprs;
+                  )*
+            }
+        }
+    ))
+}
diff --git a/src/mul_helpers.rs b/src/mul_helpers.rs
new file mode 100644 (file)
index 0000000..985d64f
--- /dev/null
@@ -0,0 +1,36 @@
+use crate::utils::{add_where_clauses_for_new_ident, MultiFieldData, RefType};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{Generics, Ident};
+
+pub fn generics_and_exprs(
+    multi_field_data: MultiFieldData,
+    scalar_ident: &Ident,
+    type_where_clauses: TokenStream,
+    ref_type: RefType,
+) -> (Generics, Vec<TokenStream>) {
+    let MultiFieldData {
+        fields,
+        casted_traits,
+        members,
+        method_ident,
+        ..
+    } = multi_field_data;
+    let reference = ref_type.reference();
+    let exprs: Vec<_> = casted_traits
+        .iter()
+        .zip(members)
+        .map(
+            |(casted_trait, member)| quote!(#casted_trait::#method_ident(#reference #member, rhs)),
+        )
+        .collect();
+
+    let new_generics = add_where_clauses_for_new_ident(
+        &multi_field_data.state.input.generics,
+        &fields,
+        scalar_ident,
+        type_where_clauses,
+        true,
+    );
+    (new_generics, exprs)
+}
diff --git a/src/mul_like.rs b/src/mul_like.rs
new file mode 100644 (file)
index 0000000..734dc0f
--- /dev/null
@@ -0,0 +1,62 @@
+use crate::add_like;
+use crate::mul_helpers::generics_and_exprs;
+use crate::utils::{AttrParams, MultiFieldData, RefType, State};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use std::collections::HashSet;
+use std::iter;
+use syn::{DeriveInput, Ident, Result};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let mut state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(::core::ops),
+        trait_name.to_lowercase(),
+        AttrParams::struct_(vec!["forward"]),
+    )?;
+    if state.default_info.forward {
+        return Ok(add_like::expand(input, trait_name));
+    }
+
+    let scalar_ident = &Ident::new("__RhsT", Span::call_site());
+    state.add_trait_path_type_param(quote!(#scalar_ident));
+    let multi_field_data = state.enabled_fields_data();
+    let MultiFieldData {
+        input_type,
+        field_types,
+        ty_generics,
+        trait_path,
+        trait_path_with_params,
+        method_ident,
+        ..
+    } = multi_field_data.clone();
+
+    let tys = field_types.iter().collect::<HashSet<_>>();
+    let tys = tys.iter();
+    let scalar_iter = iter::repeat(scalar_ident);
+    let trait_path_iter = iter::repeat(trait_path);
+
+    let type_where_clauses = quote! {
+        where #(#tys: #trait_path_iter<#scalar_iter, Output=#tys>),*
+    };
+
+    let (generics, initializers) = generics_and_exprs(
+        multi_field_data.clone(),
+        scalar_ident,
+        type_where_clauses,
+        RefType::No,
+    );
+    let body = multi_field_data.initializer(&initializers);
+    let (impl_generics, _, where_clause) = generics.split_for_impl();
+    Ok(quote!(
+        impl#impl_generics  #trait_path_with_params for #input_type#ty_generics #where_clause {
+            type Output = #input_type#ty_generics;
+            #[inline]
+            fn #method_ident(self, rhs: #scalar_ident) -> #input_type#ty_generics {
+                #body
+            }
+        }
+
+    ))
+}
diff --git a/src/not_like.rs b/src/not_like.rs
new file mode 100644 (file)
index 0000000..2639c3b
--- /dev/null
@@ -0,0 +1,166 @@
+use crate::utils::{
+    add_extra_type_param_bound_op_output, named_to_vec, unnamed_to_vec,
+};
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, ToTokens};
+use std::iter;
+use syn::{Data, DataEnum, DeriveInput, Field, Fields, Ident, Index};
+
+pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream {
+    let trait_ident = Ident::new(trait_name, Span::call_site());
+    let method_name = trait_name.to_lowercase();
+    let method_ident = &Ident::new(&method_name, Span::call_site());
+    let input_type = &input.ident;
+
+    let generics = add_extra_type_param_bound_op_output(&input.generics, &trait_ident);
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let (output_type, block) = match input.data {
+        Data::Struct(ref data_struct) => match data_struct.fields {
+            Fields::Unnamed(ref fields) => (
+                quote!(#input_type#ty_generics),
+                tuple_content(input_type, &unnamed_to_vec(fields), method_ident),
+            ),
+            Fields::Named(ref fields) => (
+                quote!(#input_type#ty_generics),
+                struct_content(input_type, &named_to_vec(fields), method_ident),
+            ),
+            _ => panic!("Unit structs cannot use derive({})", trait_name),
+        },
+        Data::Enum(ref data_enum) => {
+            enum_output_type_and_content(input, data_enum, method_ident)
+        }
+
+        _ => panic!("Only structs and enums can use derive({})", trait_name),
+    };
+
+    quote!(
+        impl#impl_generics ::core::ops::#trait_ident for #input_type#ty_generics #where_clause {
+            type Output = #output_type;
+            #[inline]
+            fn #method_ident(self) -> #output_type {
+                #block
+            }
+        }
+    )
+}
+
+fn tuple_content<T: ToTokens>(
+    input_type: &T,
+    fields: &[&Field],
+    method_ident: &Ident,
+) -> TokenStream {
+    let mut exprs = vec![];
+
+    for i in 0..fields.len() {
+        let i = Index::from(i);
+        // generates `self.0.add()`
+        let expr = quote!(self.#i.#method_ident());
+        exprs.push(expr);
+    }
+
+    quote!(#input_type(#(#exprs),*))
+}
+
+fn struct_content(
+    input_type: &Ident,
+    fields: &[&Field],
+    method_ident: &Ident,
+) -> TokenStream {
+    let mut exprs = vec![];
+
+    for field in fields {
+        // It's safe to unwrap because struct fields always have an identifier
+        let field_id = field.ident.as_ref();
+        // generates `x: self.x.not()`
+        let expr = quote!(#field_id: self.#field_id.#method_ident());
+        exprs.push(expr)
+    }
+
+    quote!(#input_type{#(#exprs),*})
+}
+
+fn enum_output_type_and_content(
+    input: &DeriveInput,
+    data_enum: &DataEnum,
+    method_ident: &Ident,
+) -> (TokenStream, TokenStream) {
+    let input_type = &input.ident;
+    let (_, ty_generics, _) = input.generics.split_for_impl();
+    let mut matches = vec![];
+    let mut method_iter = iter::repeat(method_ident);
+    // If the enum contains unit types that means it can error.
+    let has_unit_type = data_enum.variants.iter().any(|v| v.fields == Fields::Unit);
+
+    for variant in &data_enum.variants {
+        let subtype = &variant.ident;
+        let subtype = quote!(#input_type::#subtype);
+
+        match variant.fields {
+            Fields::Unnamed(ref fields) => {
+                // The patern that is outputted should look like this:
+                // (Subtype(vars)) => Ok(TypePath(exprs))
+                let size = unnamed_to_vec(fields).len();
+                let vars: &Vec<_> = &(0..size)
+                    .map(|i| Ident::new(&format!("__{}", i), Span::call_site()))
+                    .collect();
+                let method_iter = method_iter.by_ref();
+                let mut body = quote!(#subtype(#(#vars.#method_iter()),*));
+                if has_unit_type {
+                    body = quote!(::core::result::Result::Ok(#body))
+                }
+                let matcher = quote! {
+                    #subtype(#(#vars),*) => {
+                        #body
+                    }
+                };
+                matches.push(matcher);
+            }
+            Fields::Named(ref fields) => {
+                // The patern that is outputted should look like this:
+                // (Subtype{a: __l_a, ...} => {
+                //     Ok(Subtype{a: __l_a.neg(__r_a), ...})
+                // }
+                let field_vec = named_to_vec(fields);
+                let size = field_vec.len();
+                let field_names: &Vec<_> = &field_vec
+                    .iter()
+                    .map(|f| f.ident.as_ref().unwrap())
+                    .collect();
+                let vars: &Vec<_> = &(0..size)
+                    .map(|i| Ident::new(&format!("__{}", i), Span::call_site()))
+                    .collect();
+                let method_iter = method_iter.by_ref();
+                let mut body =
+                    quote!(#subtype{#(#field_names: #vars.#method_iter()),*});
+                if has_unit_type {
+                    body = quote!(::core::result::Result::Ok(#body))
+                }
+                let matcher = quote! {
+                    #subtype{#(#field_names: #vars),*} => {
+                        #body
+                    }
+                };
+                matches.push(matcher);
+            }
+            Fields::Unit => {
+                let message = format!("Cannot {}() unit variants", method_ident);
+                matches.push(quote!(#subtype => ::core::result::Result::Err(#message)));
+            }
+        }
+    }
+
+    let body = quote!(
+        match self {
+            #(#matches),*
+        }
+    );
+
+    let output_type = if has_unit_type {
+        quote!(::core::result::Result<#input_type#ty_generics, &'static str>)
+    } else {
+        quote!(#input_type#ty_generics)
+    };
+
+    (output_type, body)
+}
diff --git a/src/parsing.rs b/src/parsing.rs
new file mode 100644 (file)
index 0000000..528d438
--- /dev/null
@@ -0,0 +1,883 @@
+use self::RuleResult::{Failed, Matched};
+fn escape_default(s: &str) -> String {
+    s.chars().flat_map(|c| c.escape_default()).collect()
+}
+fn char_range_at(s: &str, pos: usize) -> (char, usize) {
+    let c = &s[pos..].chars().next().unwrap();
+    let next_pos = pos + c.len_utf8();
+    (*c, next_pos)
+}
+#[derive(Clone)]
+enum RuleResult<T> {
+    Matched(usize, T),
+    Failed,
+}
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ParseError {
+    pub line: usize,
+    pub column: usize,
+    pub offset: usize,
+    pub expected: ::std::collections::HashSet<&'static str>,
+}
+pub type ParseResult<T> = Result<T, ParseError>;
+impl ::std::fmt::Display for ParseError {
+    fn fmt(
+        &self,
+        fmt: &mut ::std::fmt::Formatter,
+    ) -> ::std::result::Result<(), ::std::fmt::Error> {
+        write!(fmt, "error at {}:{}: expected ", self.line, self.column)?;
+        if self.expected.len() == 0 {
+            write!(fmt, "EOF")?;
+        } else if self.expected.len() == 1 {
+            write!(
+                fmt,
+                "`{}`",
+                escape_default(self.expected.iter().next().unwrap())
+            )?;
+        } else {
+            let mut iter = self.expected.iter();
+            write!(fmt, "one of `{}`", escape_default(iter.next().unwrap()))?;
+            for elem in iter {
+                write!(fmt, ", `{}`", escape_default(elem))?;
+            }
+        }
+        Ok(())
+    }
+}
+impl ::std::error::Error for ParseError {
+    fn description(&self) -> &str {
+        "parse error"
+    }
+}
+fn slice_eq(
+    input: &str,
+    state: &mut ParseState,
+    pos: usize,
+    m: &'static str,
+) -> RuleResult<()> {
+    #![inline]
+    #![allow(dead_code)]
+    let l = m.len();
+    if input.len() >= pos + l && &input.as_bytes()[pos..pos + l] == m.as_bytes() {
+        Matched(pos + l, ())
+    } else {
+        state.mark_failure(pos, m)
+    }
+}
+fn slice_eq_case_insensitive(
+    input: &str,
+    state: &mut ParseState,
+    pos: usize,
+    m: &'static str,
+) -> RuleResult<()> {
+    #![inline]
+    #![allow(dead_code)]
+    let mut used = 0usize;
+    let mut input_iter = input[pos..].chars().flat_map(|x| x.to_uppercase());
+    for m_char_upper in m.chars().flat_map(|x| x.to_uppercase()) {
+        used += m_char_upper.len_utf8();
+        let input_char_result = input_iter.next();
+        if input_char_result.is_none() || input_char_result.unwrap() != m_char_upper {
+            return state.mark_failure(pos, m);
+        }
+    }
+    Matched(pos + used, ())
+}
+fn any_char(input: &str, state: &mut ParseState, pos: usize) -> RuleResult<()> {
+    #![inline]
+    #![allow(dead_code)]
+    if input.len() > pos {
+        let (_, next) = char_range_at(input, pos);
+        Matched(next, ())
+    } else {
+        state.mark_failure(pos, "<character>")
+    }
+}
+fn pos_to_line(input: &str, pos: usize) -> (usize, usize) {
+    let before = &input[..pos];
+    let line = before.as_bytes().iter().filter(|&&c| c == b'\n').count() + 1;
+    let col = before.chars().rev().take_while(|&c| c != '\n').count() + 1;
+    (line, col)
+}
+impl<'input> ParseState<'input> {
+    fn mark_failure(&mut self, pos: usize, expected: &'static str) -> RuleResult<()> {
+        if self.suppress_fail == 0 {
+            if pos > self.max_err_pos {
+                self.max_err_pos = pos;
+                self.expected.clear();
+            }
+            if pos == self.max_err_pos {
+                self.expected.insert(expected);
+            }
+        }
+        Failed
+    }
+}
+struct ParseState<'input> {
+    max_err_pos: usize,
+    suppress_fail: usize,
+    expected: ::std::collections::HashSet<&'static str>,
+    _phantom: ::std::marker::PhantomData<&'input ()>,
+}
+impl<'input> ParseState<'input> {
+    fn new() -> ParseState<'input> {
+        ParseState {
+            max_err_pos: 0,
+            suppress_fail: 0,
+            expected: ::std::collections::HashSet::new(),
+            _phantom: ::std::marker::PhantomData,
+        }
+    }
+}
+
+fn __parse_discard_doubles<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<Option<&'input str>> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = {
+            let __choice_res = {
+                let __seq_res = slice_eq(__input, __state, __pos, "{");
+                match __seq_res {
+                    Matched(__pos, _) => slice_eq(__input, __state, __pos, "{"),
+                    Failed => Failed,
+                }
+            };
+            match __choice_res {
+                Matched(__pos, __value) => Matched(__pos, __value),
+                Failed => {
+                    let __seq_res = slice_eq(__input, __state, __pos, "}");
+                    match __seq_res {
+                        Matched(__pos, _) => slice_eq(__input, __state, __pos, "}"),
+                        Failed => Failed,
+                    }
+                }
+            }
+        };
+        match __seq_res {
+            Matched(__pos, _) => Matched(__pos, { None }),
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_placeholder_inner<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<Option<&'input str>> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = {
+            let str_start = __pos;
+            match {
+                let __seq_res = if __input.len() > __pos {
+                    let (__ch, __next) = char_range_at(__input, __pos);
+                    match __ch {
+                        '{' => Matched(__next, ()),
+                        _ => __state.mark_failure(__pos, "[{]"),
+                    }
+                } else {
+                    __state.mark_failure(__pos, "[{]")
+                };
+                match __seq_res {
+                    Matched(__pos, _) => {
+                        let __seq_res = {
+                            let mut __repeat_pos = __pos;
+                            loop {
+                                let __pos = __repeat_pos;
+                                let __step_res = {
+                                    let __seq_res = {
+                                        __state.suppress_fail += 1;
+                                        let __assert_res = if __input.len() > __pos {
+                                            let (__ch, __next) =
+                                                char_range_at(__input, __pos);
+                                            match __ch {
+                                                '{' | '}' => Matched(__next, ()),
+                                                _ => {
+                                                    __state.mark_failure(__pos, "[{}]")
+                                                }
+                                            }
+                                        } else {
+                                            __state.mark_failure(__pos, "[{}]")
+                                        };
+                                        __state.suppress_fail -= 1;
+                                        match __assert_res {
+                                            Failed => Matched(__pos, ()),
+                                            Matched(..) => Failed,
+                                        }
+                                    };
+                                    match __seq_res {
+                                        Matched(__pos, _) => {
+                                            any_char(__input, __state, __pos)
+                                        }
+                                        Failed => Failed,
+                                    }
+                                };
+                                match __step_res {
+                                    Matched(__newpos, __value) => {
+                                        __repeat_pos = __newpos;
+                                    }
+                                    Failed => {
+                                        break;
+                                    }
+                                }
+                            }
+                            Matched(__repeat_pos, ())
+                        };
+                        match __seq_res {
+                            Matched(__pos, _) => {
+                                if __input.len() > __pos {
+                                    let (__ch, __next) = char_range_at(__input, __pos);
+                                    match __ch {
+                                        '}' => Matched(__next, ()),
+                                        _ => __state.mark_failure(__pos, "[}]"),
+                                    }
+                                } else {
+                                    __state.mark_failure(__pos, "[}]")
+                                }
+                            }
+                            Failed => Failed,
+                        }
+                    }
+                    Failed => Failed,
+                }
+            } {
+                Matched(__newpos, _) => {
+                    Matched(__newpos, &__input[str_start..__newpos])
+                }
+                Failed => Failed,
+            }
+        };
+        match __seq_res {
+            Matched(__pos, n) => Matched(__pos, { Some(n) }),
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_discard_any<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<Option<&'input str>> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = any_char(__input, __state, __pos);
+        match __seq_res {
+            Matched(__pos, _) => Matched(__pos, { None }),
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_arg<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<usize> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = {
+            let str_start = __pos;
+            match {
+                let mut __repeat_pos = __pos;
+                let mut __repeat_value = vec![];
+                loop {
+                    let __pos = __repeat_pos;
+                    let __step_res = if __input.len() > __pos {
+                        let (__ch, __next) = char_range_at(__input, __pos);
+                        match __ch {
+                            '0'...'9' => Matched(__next, ()),
+                            _ => __state.mark_failure(__pos, "[0-9]"),
+                        }
+                    } else {
+                        __state.mark_failure(__pos, "[0-9]")
+                    };
+                    match __step_res {
+                        Matched(__newpos, __value) => {
+                            __repeat_pos = __newpos;
+                            __repeat_value.push(__value);
+                        }
+                        Failed => {
+                            break;
+                        }
+                    }
+                }
+                if __repeat_value.len() >= 1 {
+                    Matched(__repeat_pos, ())
+                } else {
+                    Failed
+                }
+            } {
+                Matched(__newpos, _) => {
+                    Matched(__newpos, &__input[str_start..__newpos])
+                }
+                Failed => Failed,
+            }
+        };
+        match __seq_res {
+            Matched(__pos, n) => Matched(__pos, { n.parse().unwrap() }),
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_ty<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<&'input str> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = {
+            let str_start = __pos;
+            match {
+                let __choice_res = {
+                    let __choice_res = slice_eq(__input, __state, __pos, "x?");
+                    match __choice_res {
+                        Matched(__pos, __value) => Matched(__pos, __value),
+                        Failed => slice_eq(__input, __state, __pos, "X?"),
+                    }
+                };
+                match __choice_res {
+                    Matched(__pos, __value) => Matched(__pos, __value),
+                    Failed => {
+                        let __choice_res = slice_eq(__input, __state, __pos, "o");
+                        match __choice_res {
+                            Matched(__pos, __value) => Matched(__pos, __value),
+                            Failed => {
+                                let __choice_res =
+                                    slice_eq(__input, __state, __pos, "x");
+                                match __choice_res {
+                                    Matched(__pos, __value) => Matched(__pos, __value),
+                                    Failed => {
+                                        let __choice_res =
+                                            slice_eq(__input, __state, __pos, "X");
+                                        match __choice_res {
+                                            Matched(__pos, __value) => {
+                                                Matched(__pos, __value)
+                                            }
+                                            Failed => {
+                                                let __choice_res = slice_eq(
+                                                    __input, __state, __pos, "p",
+                                                );
+                                                match __choice_res {
+                                                    Matched(__pos, __value) => {
+                                                        Matched(__pos, __value)
+                                                    }
+                                                    Failed => {
+                                                        let __choice_res = slice_eq(
+                                                            __input, __state, __pos,
+                                                            "b",
+                                                        );
+                                                        match __choice_res {
+                                                            Matched(__pos, __value) => {
+                                                                Matched(__pos, __value)
+                                                            }
+                                                            Failed => {
+                                                                let __choice_res =
+                                                                    slice_eq(
+                                                                        __input,
+                                                                        __state, __pos,
+                                                                        "e",
+                                                                    );
+                                                                match __choice_res {
+                                                                    Matched(
+                                                                        __pos,
+                                                                        __value,
+                                                                    ) => Matched(
+                                                                        __pos, __value,
+                                                                    ),
+                                                                    Failed => {
+                                                                        let __choice_res =
+                                                                            slice_eq(
+                                                                                __input,
+                                                                                __state,
+                                                                                __pos,
+                                                                                "E",
+                                                                            );
+                                                                        match __choice_res { Matched ( __pos , __value ) => Matched ( __pos , __value ) , Failed => slice_eq ( __input , __state , __pos , "?" ) }
+                                                                    }
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            } {
+                Matched(__newpos, _) => {
+                    Matched(__newpos, &__input[str_start..__newpos])
+                }
+                Failed => Failed,
+            }
+        };
+        match __seq_res {
+            Matched(__pos, n) => Matched(__pos, { n }),
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_format_spec<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<Option<&'input str>> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = slice_eq(__input, __state, __pos, ":");
+        match __seq_res {
+            Matched(__pos, _) => {
+                let __seq_res = match {
+                    let __seq_res = match {
+                        let __seq_res = {
+                            __state.suppress_fail += 1;
+                            let __assert_res = if __input.len() > __pos {
+                                let (__ch, __next) = char_range_at(__input, __pos);
+                                match __ch {
+                                    '<' | '^' | '>' => Matched(__next, ()),
+                                    _ => __state.mark_failure(__pos, "[<^>]"),
+                                }
+                            } else {
+                                __state.mark_failure(__pos, "[<^>]")
+                            };
+                            __state.suppress_fail -= 1;
+                            match __assert_res {
+                                Failed => Matched(__pos, ()),
+                                Matched(..) => Failed,
+                            }
+                        };
+                        match __seq_res {
+                            Matched(__pos, _) => any_char(__input, __state, __pos),
+                            Failed => Failed,
+                        }
+                    } {
+                        Matched(__newpos, _) => Matched(__newpos, ()),
+                        Failed => Matched(__pos, ()),
+                    };
+                    match __seq_res {
+                        Matched(__pos, _) => {
+                            if __input.len() > __pos {
+                                let (__ch, __next) = char_range_at(__input, __pos);
+                                match __ch {
+                                    '<' | '^' | '>' => Matched(__next, ()),
+                                    _ => __state.mark_failure(__pos, "[<^>]"),
+                                }
+                            } else {
+                                __state.mark_failure(__pos, "[<^>]")
+                            }
+                        }
+                        Failed => Failed,
+                    }
+                } {
+                    Matched(__newpos, _) => Matched(__newpos, ()),
+                    Failed => Matched(__pos, ()),
+                };
+                match __seq_res {
+                    Matched(__pos, _) => {
+                        let __seq_res = match {
+                            let __choice_res = slice_eq(__input, __state, __pos, "+");
+                            match __choice_res {
+                                Matched(__pos, __value) => Matched(__pos, __value),
+                                Failed => slice_eq(__input, __state, __pos, "-"),
+                            }
+                        } {
+                            Matched(__newpos, _) => Matched(__newpos, ()),
+                            Failed => Matched(__pos, ()),
+                        };
+                        match __seq_res {
+                            Matched(__pos, _) => {
+                                let __seq_res =
+                                    match slice_eq(__input, __state, __pos, "#") {
+                                        Matched(__newpos, _) => Matched(__newpos, ()),
+                                        Failed => Matched(__pos, ()),
+                                    };
+                                match __seq_res {
+                                    Matched(__pos, _) => {
+                                        let __seq_res = match {
+                                            let __choice_res = {
+                                                let __seq_res = {
+                                                    let mut __repeat_pos = __pos;
+                                                    let mut __repeat_value = vec![];
+                                                    loop {
+                                                        let __pos = __repeat_pos;
+                                                        let __step_res =
+                                                            if __input.len() > __pos {
+                                                                let (__ch, __next) =
+                                                                    char_range_at(
+                                                                        __input, __pos,
+                                                                    );
+                                                                match __ch {
+                                                                    'A'...'Z'
+                                                                    | 'a'...'z'
+                                                                    | '0'...'9'
+                                                                    | '_' => Matched(
+                                                                        __next,
+                                                                        (),
+                                                                    ),
+                                                                    _ => __state
+                                                                        .mark_failure(
+                                                                        __pos,
+                                                                        "[A-Za-z0-9_]",
+                                                                    ),
+                                                                }
+                                                            } else {
+                                                                __state.mark_failure(
+                                                                    __pos,
+                                                                    "[A-Za-z0-9_]",
+                                                                )
+                                                            };
+                                                        match __step_res {
+                                                            Matched(
+                                                                __newpos,
+                                                                __value,
+                                                            ) => {
+                                                                __repeat_pos = __newpos;
+                                                                __repeat_value
+                                                                    .push(__value);
+                                                            }
+                                                            Failed => {
+                                                                break;
+                                                            }
+                                                        }
+                                                    }
+                                                    if __repeat_value.len() >= 1 {
+                                                        Matched(__repeat_pos, ())
+                                                    } else {
+                                                        Failed
+                                                    }
+                                                };
+                                                match __seq_res {
+                                                    Matched(__pos, _) => slice_eq(
+                                                        __input, __state, __pos, "$",
+                                                    ),
+                                                    Failed => Failed,
+                                                }
+                                            };
+                                            match __choice_res {
+                                                Matched(__pos, __value) => {
+                                                    Matched(__pos, __value)
+                                                }
+                                                Failed => {
+                                                    let mut __repeat_pos = __pos;
+                                                    let mut __repeat_value = vec![];
+                                                    loop {
+                                                        let __pos = __repeat_pos;
+                                                        let __step_res = if __input
+                                                            .len()
+                                                            > __pos
+                                                        {
+                                                            let (__ch, __next) =
+                                                                char_range_at(
+                                                                    __input, __pos,
+                                                                );
+                                                            match __ch {
+                                                                '0'...'9' => {
+                                                                    Matched(__next, ())
+                                                                }
+                                                                _ => __state
+                                                                    .mark_failure(
+                                                                        __pos, "[0-9]",
+                                                                    ),
+                                                            }
+                                                        } else {
+                                                            __state.mark_failure(
+                                                                __pos, "[0-9]",
+                                                            )
+                                                        };
+                                                        match __step_res {
+                                                            Matched(
+                                                                __newpos,
+                                                                __value,
+                                                            ) => {
+                                                                __repeat_pos = __newpos;
+                                                                __repeat_value
+                                                                    .push(__value);
+                                                            }
+                                                            Failed => {
+                                                                break;
+                                                            }
+                                                        }
+                                                    }
+                                                    if __repeat_value.len() >= 1 {
+                                                        Matched(__repeat_pos, ())
+                                                    } else {
+                                                        Failed
+                                                    }
+                                                }
+                                            }
+                                        } {
+                                            Matched(__newpos, _) => {
+                                                Matched(__newpos, ())
+                                            }
+                                            Failed => Matched(__pos, ()),
+                                        };
+                                        match __seq_res {
+                                            Matched(__pos, _) => {
+                                                let __seq_res = match slice_eq(
+                                                    __input, __state, __pos, "0",
+                                                ) {
+                                                    Matched(__newpos, _) => {
+                                                        Matched(__newpos, ())
+                                                    }
+                                                    Failed => Matched(__pos, ()),
+                                                };
+                                                match __seq_res {
+                                                    Matched(__pos, _) => {
+                                                        let __seq_res = match {
+                                                            let __seq_res = slice_eq(
+                                                                __input, __state,
+                                                                __pos, ".",
+                                                            );
+                                                            match __seq_res {
+                                                                Matched(__pos, _) => {
+                                                                    let __choice_res = {
+                                                                        let __seq_res = {
+                                                                            let mut
+                                                                            __repeat_pos =
+                                                                                __pos;
+                                                                            let mut
+                                                                            __repeat_value =
+                                                                                vec![];
+                                                                            loop {
+                                                                                let __pos = __repeat_pos ;
+                                                                                let __step_res = if __input . len ( ) > __pos { let ( __ch , __next ) = char_range_at ( __input , __pos ) ; match __ch { 'A' ... 'Z' | 'a' ... 'z' | '0' ... '9' | '_' => Matched ( __next , ( ) ) , _ => __state . mark_failure ( __pos , "[A-Za-z0-9_]" ) , } } else { __state . mark_failure ( __pos , "[A-Za-z0-9_]" ) } ;
+                                                                                match __step_res { Matched ( __newpos , __value ) => { __repeat_pos = __newpos ; __repeat_value . push ( __value ) ; } , Failed => { break ; } }
+                                                                            }
+                                                                            if __repeat_value . len ( ) >= 1 { Matched ( __repeat_pos , ( ) ) } else { Failed }
+                                                                        };
+                                                                        match __seq_res { Matched ( __pos , _ ) => { slice_eq ( __input , __state , __pos , "$" ) } Failed => Failed , }
+                                                                    };
+                                                                    match __choice_res {
+                                                                        Matched(
+                                                                            __pos,
+                                                                            __value,
+                                                                        ) => Matched(
+                                                                            __pos,
+                                                                            __value,
+                                                                        ),
+                                                                        Failed => {
+                                                                            let __choice_res = {
+                                                                                let mut __repeat_pos = __pos ;
+                                                                                let mut
+                                                                                __repeat_value = vec![];
+                                                                                loop {
+                                                                                    let __pos = __repeat_pos ;
+                                                                                    let __step_res = if __input . len ( ) > __pos { let ( __ch , __next ) = char_range_at ( __input , __pos ) ; match __ch { '0' ... '9' => Matched ( __next , ( ) ) , _ => __state . mark_failure ( __pos , "[0-9]" ) , } } else { __state . mark_failure ( __pos , "[0-9]" ) } ;
+                                                                                    match __step_res { Matched ( __newpos , __value ) => { __repeat_pos = __newpos ; __repeat_value . push ( __value ) ; } , Failed => { break ; } }
+                                                                                }
+                                                                                if __repeat_value . len ( ) >= 1 { Matched ( __repeat_pos , ( ) ) } else { Failed }
+                                                                            };
+                                                                            match __choice_res { Matched ( __pos , __value ) => Matched ( __pos , __value ) , Failed => slice_eq ( __input , __state , __pos , "*" ) }
+                                                                        }
+                                                                    }
+                                                                }
+                                                                Failed => Failed,
+                                                            }
+                                                        } {
+                                                            Matched(__newpos, _) => {
+                                                                Matched(__newpos, ())
+                                                            }
+                                                            Failed => {
+                                                                Matched(__pos, ())
+                                                            }
+                                                        };
+                                                        match __seq_res {
+                                                            Matched(__pos, _) => {
+                                                                let __seq_res =
+                                                                    match __parse_ty(
+                                                                        __input,
+                                                                        __state, __pos,
+                                                                    ) {
+                                                                        Matched(
+                                                                            __newpos,
+                                                                            __value,
+                                                                        ) => Matched(
+                                                                            __newpos,
+                                                                            Some(
+                                                                                __value,
+                                                                            ),
+                                                                        ),
+                                                                        Failed => {
+                                                                            Matched(
+                                                                                __pos,
+                                                                                None,
+                                                                            )
+                                                                        }
+                                                                    };
+                                                                match __seq_res {
+                                                                    Matched(
+                                                                        __pos,
+                                                                        n,
+                                                                    ) => Matched(
+                                                                        __pos,
+                                                                        { n },
+                                                                    ),
+                                                                    Failed => Failed,
+                                                                }
+                                                            }
+                                                            Failed => Failed,
+                                                        }
+                                                    }
+                                                    Failed => Failed,
+                                                }
+                                            }
+                                            Failed => Failed,
+                                        }
+                                    }
+                                    Failed => Failed,
+                                }
+                            }
+                            Failed => Failed,
+                        }
+                    }
+                    Failed => Failed,
+                }
+            }
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_all_placeholders<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<Vec<&'input str>> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = {
+            let mut __repeat_pos = __pos;
+            let mut __repeat_value = vec![];
+            loop {
+                let __pos = __repeat_pos;
+                let __step_res = {
+                    let __choice_res = __parse_discard_doubles(__input, __state, __pos);
+                    match __choice_res {
+                        Matched(__pos, __value) => Matched(__pos, __value),
+                        Failed => {
+                            let __choice_res =
+                                __parse_placeholder_inner(__input, __state, __pos);
+                            match __choice_res {
+                                Matched(__pos, __value) => Matched(__pos, __value),
+                                Failed => __parse_discard_any(__input, __state, __pos),
+                            }
+                        }
+                    }
+                };
+                match __step_res {
+                    Matched(__newpos, __value) => {
+                        __repeat_pos = __newpos;
+                        __repeat_value.push(__value);
+                    }
+                    Failed => {
+                        break;
+                    }
+                }
+            }
+            Matched(__repeat_pos, __repeat_value)
+        };
+        match __seq_res {
+            Matched(__pos, x) => {
+                Matched(__pos, { x.into_iter().flat_map(|x| x).collect() })
+            }
+            Failed => Failed,
+        }
+    }
+}
+
+fn __parse_format<'input>(
+    __input: &'input str,
+    __state: &mut ParseState<'input>,
+    __pos: usize,
+) -> RuleResult<(Option<usize>, Option<&'input str>)> {
+    #![allow(non_snake_case, unused)]
+    {
+        let __seq_res = slice_eq(__input, __state, __pos, "{");
+        match __seq_res {
+            Matched(__pos, _) => {
+                let __seq_res = match __parse_arg(__input, __state, __pos) {
+                    Matched(__newpos, __value) => Matched(__newpos, Some(__value)),
+                    Failed => Matched(__pos, None),
+                };
+                match __seq_res {
+                    Matched(__pos, n) => {
+                        let __seq_res =
+                            match __parse_format_spec(__input, __state, __pos) {
+                                Matched(__newpos, __value) => {
+                                    Matched(__newpos, Some(__value))
+                                }
+                                Failed => Matched(__pos, None),
+                            };
+                        match __seq_res {
+                            Matched(__pos, o) => {
+                                let __seq_res = slice_eq(__input, __state, __pos, "}");
+                                match __seq_res {
+                                    Matched(__pos, _) => {
+                                        Matched(__pos, { (n, o.and_then(|x| x)) })
+                                    }
+                                    Failed => Failed,
+                                }
+                            }
+                            Failed => Failed,
+                        }
+                    }
+                    Failed => Failed,
+                }
+            }
+            Failed => Failed,
+        }
+    }
+}
+
+pub fn all_placeholders<'input>(__input: &'input str) -> ParseResult<Vec<&'input str>> {
+    #![allow(non_snake_case, unused)]
+    let mut __state = ParseState::new();
+    match __parse_all_placeholders(__input, &mut __state, 0) {
+        Matched(__pos, __value) => {
+            if __pos == __input.len() {
+                return Ok(__value);
+            }
+        }
+        _ => {}
+    }
+    let (__line, __col) = pos_to_line(__input, __state.max_err_pos);
+    Err(ParseError {
+        line: __line,
+        column: __col,
+        offset: __state.max_err_pos,
+        expected: __state.expected,
+    })
+}
+
+pub fn format<'input>(
+    __input: &'input str,
+) -> ParseResult<(Option<usize>, Option<&'input str>)> {
+    #![allow(non_snake_case, unused)]
+    let mut __state = ParseState::new();
+    match __parse_format(__input, &mut __state, 0) {
+        Matched(__pos, __value) => {
+            if __pos == __input.len() {
+                return Ok(__value);
+            }
+        }
+        _ => {}
+    }
+    let (__line, __col) = pos_to_line(__input, __state.max_err_pos);
+    Err(ParseError {
+        line: __line,
+        column: __col,
+        offset: __state.max_err_pos,
+        expected: __state.expected,
+    })
+}
diff --git a/src/sum_like.rs b/src/sum_like.rs
new file mode 100644 (file)
index 0000000..f8f8a86
--- /dev/null
@@ -0,0 +1,56 @@
+use crate::utils::{
+    add_extra_ty_param_bound, add_extra_where_clauses, MultiFieldData, State,
+};
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{DeriveInput, Ident, Result};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::new(
+        input,
+        trait_name,
+        quote!(::core::iter),
+        trait_name.to_lowercase(),
+    )?;
+    let multi_field_data = state.enabled_fields_data();
+    let MultiFieldData {
+        input_type,
+        field_types,
+        trait_path,
+        method_ident,
+        ..
+    } = multi_field_data.clone();
+
+    let op_trait_name = if trait_name == "Sum" { "Add" } else { "Mul" };
+    let op_trait_ident = Ident::new(op_trait_name, Span::call_site());
+    let op_path = quote!(::core::ops::#op_trait_ident);
+    let op_method_ident =
+        Ident::new(&(op_trait_name.to_lowercase()), Span::call_site());
+    let has_type_params = input.generics.type_params().next().is_none();
+    let generics = if has_type_params {
+        input.generics.clone()
+    } else {
+        let (_, ty_generics, _) = input.generics.split_for_impl();
+        let generics = add_extra_ty_param_bound(&input.generics, trait_path);
+        let operator_where_clause = quote! {
+            where #input_type#ty_generics: #op_path<Output=#input_type#ty_generics>
+        };
+        add_extra_where_clauses(&generics, operator_where_clause)
+    };
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let initializers: Vec<_> = field_types
+        .iter()
+        .map(|field_type| quote!(#trait_path::#method_ident(::core::iter::empty::<#field_type>())))
+        .collect();
+    let identity = multi_field_data.initializer(&initializers);
+
+    Ok(quote!(
+        impl#impl_generics #trait_path for #input_type#ty_generics #where_clause {
+            #[inline]
+            fn #method_ident<I: ::core::iter::Iterator<Item = Self>>(iter: I) -> Self {
+                iter.fold(#identity, #op_path::#op_method_ident)
+            }
+        }
+    ))
+}
diff --git a/src/try_into.rs b/src/try_into.rs
new file mode 100644 (file)
index 0000000..996bcdf
--- /dev/null
@@ -0,0 +1,122 @@
+use crate::utils::{
+    add_extra_generic_param, numbered_vars, AttrParams, DeriveType, MultiFieldData,
+    State,
+};
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::{DeriveInput, Result};
+
+use crate::utils::HashMap;
+
+/// Provides the hook to expand `#[derive(TryInto)]` into an implementation of `TryInto`
+#[allow(clippy::cognitive_complexity)]
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(::core::convert),
+        String::from("try_into"),
+        AttrParams {
+            enum_: vec!["ignore", "owned", "ref", "ref_mut"],
+            variant: vec!["ignore", "owned", "ref", "ref_mut"],
+            struct_: vec!["ignore", "owned", "ref", "ref_mut"],
+            field: vec!["ignore"],
+        },
+    )?;
+    assert!(
+        state.derive_type == DeriveType::Enum,
+        "Only enums can derive TryInto"
+    );
+
+    let mut variants_per_types = HashMap::default();
+
+    for variant_state in state.enabled_variant_data().variant_states {
+        let multi_field_data = variant_state.enabled_fields_data();
+        let MultiFieldData {
+            variant_info,
+            field_types,
+            ..
+        } = multi_field_data.clone();
+        for ref_type in variant_info.ref_types() {
+            variants_per_types
+                .entry((ref_type, field_types.clone()))
+                .or_insert_with(Vec::new)
+                .push(multi_field_data.clone());
+        }
+    }
+
+    let mut tokens = TokenStream::new();
+
+    for ((ref_type, ref original_types), ref multi_field_datas) in variants_per_types {
+        let input_type = &input.ident;
+
+        let pattern_ref = ref_type.pattern_ref();
+        let lifetime = ref_type.lifetime();
+        let reference_with_lifetime = ref_type.reference_with_lifetime();
+
+        let mut matchers = vec![];
+        let vars = &numbered_vars(original_types.len(), "");
+        for multi_field_data in multi_field_datas {
+            let patterns: Vec<_> =
+                vars.iter().map(|var| quote!(#pattern_ref #var)).collect();
+            matchers.push(
+                multi_field_data.matcher(&multi_field_data.field_indexes, &patterns),
+            );
+        }
+
+        let vars = if vars.len() == 1 {
+            quote!(#(#vars)*)
+        } else {
+            quote!((#(#vars),*))
+        };
+
+        let output_type = if original_types.len() == 1 {
+            format!("{}", quote!(#(#original_types)*))
+        } else {
+            let types = original_types
+                .iter()
+                .map(|t| format!("{}", quote!(#t)))
+                .collect::<Vec<_>>();
+            format!("({})", types.join(", "))
+        };
+        let variant_names = multi_field_datas
+            .iter()
+            .map(|d| {
+                format!(
+                    "{}",
+                    d.variant_name.expect("Somehow there was no variant name")
+                )
+            })
+            .collect::<Vec<_>>()
+            .join(", ");
+        let message =
+            format!("Only {} can be converted to {}", variant_names, output_type);
+
+        let generics_impl;
+        let (_, ty_generics, where_clause) = input.generics.split_for_impl();
+        let (impl_generics, _, _) = if ref_type.is_ref() {
+            generics_impl = add_extra_generic_param(&input.generics, lifetime.clone());
+            generics_impl.split_for_impl()
+        } else {
+            input.generics.split_for_impl()
+        };
+
+        let try_from = quote! {
+            impl#impl_generics ::core::convert::TryFrom<#reference_with_lifetime #input_type#ty_generics> for
+                (#(#reference_with_lifetime #original_types),*) #where_clause {
+                type Error = &'static str;
+
+                #[allow(unused_variables)]
+                #[inline]
+                fn try_from(value: #reference_with_lifetime #input_type#ty_generics) -> ::core::result::Result<Self, Self::Error> {
+                    match value {
+                        #(#matchers)|* => ::core::result::Result::Ok(#vars),
+                        _ => ::core::result::Result::Err(#message),
+                    }
+                }
+            }
+        };
+        try_from.to_tokens(&mut tokens)
+    }
+    Ok(tokens)
+}
diff --git a/src/unwrap.rs b/src/unwrap.rs
new file mode 100644 (file)
index 0000000..e2243d3
--- /dev/null
@@ -0,0 +1,101 @@
+use crate::utils::{AttrParams, DeriveType, State};
+use convert_case::{Case, Casing};
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use syn::{DeriveInput, Fields, Ident, Result};
+
+pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
+    let state = State::with_attr_params(
+        input,
+        trait_name,
+        quote!(),
+        String::from("unwrap"),
+        AttrParams {
+            enum_: vec!["ignore"],
+            variant: vec!["ignore"],
+            struct_: vec!["ignore"],
+            field: vec!["ignore"],
+        },
+    )?;
+    assert!(
+        state.derive_type == DeriveType::Enum,
+        "Unwrap can only be derived for enums"
+    );
+
+    let enum_name = &input.ident;
+    let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl();
+
+    let mut funcs = vec![];
+    for variant_state in state.enabled_variant_data().variant_states {
+        let variant = variant_state.variant.unwrap();
+        let fn_name = Ident::new(
+            &format_ident!("unwrap_{}", variant.ident)
+                .to_string()
+                .to_case(Case::Snake),
+            variant.ident.span(),
+        );
+        let variant_ident = &variant.ident;
+
+        let (data_pattern, ret_value, ret_type) = match variant.fields {
+            Fields::Named(_) => panic!("cannot unwrap anonymous records"),
+            Fields::Unnamed(ref fields) => {
+                let data_pattern =
+                    (0..fields.unnamed.len()).fold(vec![], |mut a, n| {
+                        a.push(format_ident!("field_{}", n));
+                        a
+                    });
+                let ret_type = &fields.unnamed;
+                (
+                    quote! { (#(#data_pattern),*) },
+                    quote! { (#(#data_pattern),*) },
+                    quote! { (#ret_type) },
+                )
+            }
+            Fields::Unit => (quote! {}, quote! { () }, quote! { () }),
+        };
+
+        let other_arms = state.variant_states.iter().map(|variant| {
+            variant.variant.unwrap()
+        }).filter(|variant| {
+            &variant.ident != variant_ident
+        }).map(|variant| {
+            let data_pattern = match variant.fields {
+                Fields::Named(_) => quote! { {..} },
+                Fields::Unnamed(_) => quote! { (..) },
+                Fields::Unit => quote! {},
+            };
+            let variant_ident = &variant.ident;
+            quote! { #enum_name :: #variant_ident #data_pattern =>
+                      panic!(concat!("called `", stringify!(#enum_name), "::", stringify!(#fn_name),
+                                     "()` on a `", stringify!(#variant_ident), "` value"))
+            }
+        });
+
+        // The `track-caller` feature is set by our build script based
+        // on rustc version detection, as `#[track_caller]` was
+        // stabilized in a later version (1.46) of Rust than our MSRV (1.36).
+        let track_caller = if cfg!(feature = "track-caller") {
+            quote! { #[track_caller] }
+        } else {
+            quote! {}
+        };
+        let func = quote! {
+            #track_caller
+            pub fn #fn_name(self) -> #ret_type {
+                match self {
+                    #enum_name ::#variant_ident #data_pattern => #ret_value,
+                    #(#other_arms),*
+                }
+            }
+        };
+        funcs.push(func);
+    }
+
+    let imp = quote! {
+        impl #imp_generics #enum_name #type_generics #where_clause{
+            #(#funcs)*
+        }
+    };
+
+    Ok(imp)
+}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644 (file)
index 0000000..7226470
--- /dev/null
@@ -0,0 +1,1244 @@
+#![cfg_attr(not(feature = "default"), allow(dead_code), allow(unused_mut))]
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, ToTokens};
+use syn::{
+    parse_quote, punctuated::Punctuated, spanned::Spanned, Attribute, Data,
+    DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam,
+    Generics, Ident, ImplGenerics, Index, Meta, NestedMeta, Result, Token, Type,
+    TypeGenerics, TypeParamBound, Variant, WhereClause,
+};
+
+#[derive(Clone, Copy, Default)]
+pub struct DeterministicState;
+
+impl std::hash::BuildHasher for DeterministicState {
+    type Hasher = std::collections::hash_map::DefaultHasher;
+
+    fn build_hasher(&self) -> Self::Hasher {
+        Self::Hasher::default()
+    }
+}
+
+pub type HashMap<K, V> = std::collections::HashMap<K, V, DeterministicState>;
+pub type HashSet<K> = std::collections::HashSet<K, DeterministicState>;
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
+pub enum RefType {
+    No,
+    Ref,
+    Mut,
+}
+
+impl RefType {
+    pub fn lifetime(self) -> TokenStream {
+        match self {
+            RefType::No => quote!(),
+            _ => quote!('__deriveMoreLifetime),
+        }
+    }
+
+    pub fn reference(self) -> TokenStream {
+        match self {
+            RefType::No => quote!(),
+            RefType::Ref => quote!(&),
+            RefType::Mut => quote!(&mut),
+        }
+    }
+
+    pub fn mutability(self) -> TokenStream {
+        match self {
+            RefType::Mut => quote!(mut),
+            _ => quote!(),
+        }
+    }
+
+    pub fn pattern_ref(self) -> TokenStream {
+        match self {
+            RefType::Ref => quote!(ref),
+            RefType::Mut => quote!(ref mut),
+            RefType::No => quote!(),
+        }
+    }
+
+    pub fn reference_with_lifetime(self) -> TokenStream {
+        if !self.is_ref() {
+            return quote!();
+        }
+        let lifetime = self.lifetime();
+        let mutability = self.mutability();
+        quote!(&#lifetime #mutability)
+    }
+
+    pub fn is_ref(self) -> bool {
+        match self {
+            RefType::No => false,
+            _ => true,
+        }
+    }
+
+    pub fn from_attr_name(name: &str) -> Self {
+        match name {
+            "owned" => RefType::No,
+            "ref" => RefType::Ref,
+            "ref_mut" => RefType::Mut,
+            _ => panic!("'{}' is not a RefType", name),
+        }
+    }
+}
+
+pub fn numbered_vars(count: usize, prefix: &str) -> Vec<Ident> {
+    (0..count)
+        .map(|i| Ident::new(&format!("__{}{}", prefix, i), Span::call_site()))
+        .collect()
+}
+
+pub fn field_idents<'a>(fields: &'a [&'a Field]) -> Vec<&'a Ident> {
+    fields
+        .iter()
+        .map(|f| {
+            f.ident
+                .as_ref()
+                .expect("Tried to get field names of a tuple struct")
+        })
+        .collect()
+}
+
+pub fn get_field_types_iter<'a>(
+    fields: &'a [&'a Field],
+) -> Box<dyn Iterator<Item = &'a Type> + 'a> {
+    Box::new(fields.iter().map(|f| &f.ty))
+}
+
+pub fn get_field_types<'a>(fields: &'a [&'a Field]) -> Vec<&'a Type> {
+    get_field_types_iter(fields).collect()
+}
+
+pub fn add_extra_type_param_bound_op_output<'a>(
+    generics: &'a Generics,
+    trait_ident: &'a Ident,
+) -> Generics {
+    let mut generics = generics.clone();
+    for type_param in &mut generics.type_params_mut() {
+        let type_ident = &type_param.ident;
+        let bound: TypeParamBound = parse_quote! {
+            ::core::ops::#trait_ident<Output=#type_ident>
+        };
+        type_param.bounds.push(bound)
+    }
+
+    generics
+}
+
+pub fn add_extra_ty_param_bound_op<'a>(
+    generics: &'a Generics,
+    trait_ident: &'a Ident,
+) -> Generics {
+    add_extra_ty_param_bound(generics, &quote!(::core::ops::#trait_ident))
+}
+
+pub fn add_extra_ty_param_bound<'a>(
+    generics: &'a Generics,
+    bound: &'a TokenStream,
+) -> Generics {
+    let mut generics = generics.clone();
+    let bound: TypeParamBound = parse_quote! { #bound };
+    for type_param in &mut generics.type_params_mut() {
+        type_param.bounds.push(bound.clone())
+    }
+
+    generics
+}
+
+pub fn add_extra_ty_param_bound_ref<'a>(
+    generics: &'a Generics,
+    bound: &'a TokenStream,
+    ref_type: RefType,
+) -> Generics {
+    match ref_type {
+        RefType::No => add_extra_ty_param_bound(generics, bound),
+        _ => {
+            let generics = generics.clone();
+            let idents = generics.type_params().map(|x| &x.ident);
+            let ref_with_lifetime = ref_type.reference_with_lifetime();
+            add_extra_where_clauses(
+                &generics,
+                quote!(
+                    where #(#ref_with_lifetime #idents: #bound),*
+                ),
+            )
+        }
+    }
+}
+
+pub fn add_extra_generic_param(
+    generics: &Generics,
+    generic_param: TokenStream,
+) -> Generics {
+    let generic_param: GenericParam = parse_quote! { #generic_param };
+    let mut generics = generics.clone();
+    generics.params.push(generic_param);
+
+    generics
+}
+
+pub fn add_extra_generic_type_param(
+    generics: &Generics,
+    generic_param: TokenStream,
+) -> Generics {
+    let generic_param: GenericParam = parse_quote! { #generic_param };
+    let lifetimes: Vec<GenericParam> =
+        generics.lifetimes().map(|x| x.clone().into()).collect();
+    let type_params: Vec<GenericParam> =
+        generics.type_params().map(|x| x.clone().into()).collect();
+    let const_params: Vec<GenericParam> =
+        generics.const_params().map(|x| x.clone().into()).collect();
+    let mut generics = generics.clone();
+    generics.params = Default::default();
+    generics.params.extend(lifetimes);
+    generics.params.extend(type_params);
+    generics.params.push(generic_param);
+    generics.params.extend(const_params);
+
+    generics
+}
+
+pub fn add_extra_where_clauses(
+    generics: &Generics,
+    type_where_clauses: TokenStream,
+) -> Generics {
+    let mut type_where_clauses: WhereClause = parse_quote! { #type_where_clauses };
+    let mut new_generics = generics.clone();
+    if let Some(old_where) = new_generics.where_clause {
+        type_where_clauses.predicates.extend(old_where.predicates)
+    }
+    new_generics.where_clause = Some(type_where_clauses);
+
+    new_generics
+}
+
+pub fn add_where_clauses_for_new_ident<'a>(
+    generics: &'a Generics,
+    fields: &[&'a Field],
+    type_ident: &Ident,
+    type_where_clauses: TokenStream,
+    sized: bool,
+) -> Generics {
+    let generic_param = if fields.len() > 1 {
+        quote!(#type_ident: ::core::marker::Copy)
+    } else if sized {
+        quote!(#type_ident)
+    } else {
+        quote!(#type_ident: ?::core::marker::Sized)
+    };
+
+    let generics = add_extra_where_clauses(generics, type_where_clauses);
+    add_extra_generic_type_param(&generics, generic_param)
+}
+
+pub fn unnamed_to_vec(fields: &FieldsUnnamed) -> Vec<&Field> {
+    fields.unnamed.iter().collect()
+}
+
+pub fn named_to_vec(fields: &FieldsNamed) -> Vec<&Field> {
+    fields.named.iter().collect()
+}
+
+fn panic_one_field(trait_name: &str, trait_attr: &str) -> ! {
+    panic!(
+        "derive({}) only works when forwarding to a single field. Try putting #[{}] or #[{}(ignore)] on the fields in the struct",
+        trait_name, trait_attr, trait_attr,
+    )
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum DeriveType {
+    Unnamed,
+    Named,
+    Enum,
+}
+
+pub struct State<'input> {
+    pub input: &'input DeriveInput,
+    pub trait_name: &'static str,
+    pub trait_ident: Ident,
+    pub method_ident: Ident,
+    pub trait_module: TokenStream,
+    pub trait_path: TokenStream,
+    pub trait_path_params: Vec<TokenStream>,
+    pub trait_attr: String,
+    pub derive_type: DeriveType,
+    pub fields: Vec<&'input Field>,
+    pub variants: Vec<&'input Variant>,
+    pub variant_states: Vec<State<'input>>,
+    pub variant: Option<&'input Variant>,
+    pub generics: Generics,
+    pub default_info: FullMetaInfo,
+    full_meta_infos: Vec<FullMetaInfo>,
+}
+
+#[derive(Default, Clone)]
+pub struct AttrParams {
+    pub enum_: Vec<&'static str>,
+    pub variant: Vec<&'static str>,
+    pub struct_: Vec<&'static str>,
+    pub field: Vec<&'static str>,
+}
+
+impl AttrParams {
+    pub fn new(params: Vec<&'static str>) -> AttrParams {
+        AttrParams {
+            enum_: params.clone(),
+            struct_: params.clone(),
+            variant: params.clone(),
+            field: params,
+        }
+    }
+    pub fn struct_(params: Vec<&'static str>) -> AttrParams {
+        AttrParams {
+            enum_: vec![],
+            struct_: params,
+            variant: vec![],
+            field: vec![],
+        }
+    }
+
+    pub fn ignore_and_forward() -> AttrParams {
+        AttrParams::new(vec!["ignore", "forward"])
+    }
+}
+
+impl<'input> State<'input> {
+    pub fn new<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+    ) -> Result<State<'arg_input>> {
+        State::new_impl(
+            input,
+            trait_name,
+            trait_module,
+            trait_attr,
+            AttrParams::default(),
+            true,
+        )
+    }
+
+    pub fn with_field_ignore<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+    ) -> Result<State<'arg_input>> {
+        State::new_impl(
+            input,
+            trait_name,
+            trait_module,
+            trait_attr,
+            AttrParams::new(vec!["ignore"]),
+            true,
+        )
+    }
+
+    pub fn with_field_ignore_and_forward<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+    ) -> Result<State<'arg_input>> {
+        State::new_impl(
+            input,
+            trait_name,
+            trait_module,
+            trait_attr,
+            AttrParams::new(vec!["ignore", "forward"]),
+            true,
+        )
+    }
+
+    pub fn with_field_ignore_and_refs<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+    ) -> Result<State<'arg_input>> {
+        State::new_impl(
+            input,
+            trait_name,
+            trait_module,
+            trait_attr,
+            AttrParams::new(vec!["ignore", "owned", "ref", "ref_mut"]),
+            true,
+        )
+    }
+
+    pub fn with_attr_params<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+        allowed_attr_params: AttrParams,
+    ) -> Result<State<'arg_input>> {
+        State::new_impl(
+            input,
+            trait_name,
+            trait_module,
+            trait_attr,
+            allowed_attr_params,
+            true,
+        )
+    }
+
+    pub fn with_type_bound<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+        allowed_attr_params: AttrParams,
+        add_type_bound: bool,
+    ) -> Result<State<'arg_input>> {
+        Self::new_impl(
+            input,
+            trait_name,
+            trait_module,
+            trait_attr,
+            allowed_attr_params,
+            add_type_bound,
+        )
+    }
+
+    fn new_impl<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+        allowed_attr_params: AttrParams,
+        add_type_bound: bool,
+    ) -> Result<State<'arg_input>> {
+        let trait_name = trait_name.trim_end_matches("ToInner");
+        let trait_ident = Ident::new(trait_name, Span::call_site());
+        let method_ident = Ident::new(&trait_attr, Span::call_site());
+        let trait_path = quote!(#trait_module::#trait_ident);
+        let (derive_type, fields, variants): (_, Vec<_>, Vec<_>) = match input.data {
+            Data::Struct(ref data_struct) => match data_struct.fields {
+                Fields::Unnamed(ref fields) => {
+                    (DeriveType::Unnamed, unnamed_to_vec(fields), vec![])
+                }
+
+                Fields::Named(ref fields) => {
+                    (DeriveType::Named, named_to_vec(fields), vec![])
+                }
+                Fields::Unit => (DeriveType::Named, vec![], vec![]),
+            },
+            Data::Enum(ref data_enum) => (
+                DeriveType::Enum,
+                vec![],
+                data_enum.variants.iter().collect(),
+            ),
+            Data::Union(_) => {
+                panic!("cannot derive({}) for union", trait_name)
+            }
+        };
+        let attrs: Vec<_> = if derive_type == DeriveType::Enum {
+            variants.iter().map(|v| &v.attrs).collect()
+        } else {
+            fields.iter().map(|f| &f.attrs).collect()
+        };
+
+        let (allowed_attr_params_outer, allowed_attr_params_inner) =
+            if derive_type == DeriveType::Enum {
+                (&allowed_attr_params.enum_, &allowed_attr_params.variant)
+            } else {
+                (&allowed_attr_params.struct_, &allowed_attr_params.field)
+            };
+
+        let struct_meta_info =
+            get_meta_info(&trait_attr, &input.attrs, allowed_attr_params_outer)?;
+        let meta_infos: Result<Vec<_>> = attrs
+            .iter()
+            .map(|attrs| get_meta_info(&trait_attr, attrs, allowed_attr_params_inner))
+            .collect();
+        let meta_infos = meta_infos?;
+        let first_match = meta_infos
+            .iter()
+            .filter_map(|info| info.enabled.map(|_| info))
+            .next();
+
+        // Default to enabled true, except when first attribute has explicit
+        // enabling.
+        //
+        // Except for derive Error.
+        //
+        // The way `else` case works is that if any field have any valid
+        // attribute specified, then all fields without any attributes
+        // specified are filtered out from `State::enabled_fields`.
+        //
+        // However, derive Error *infers* fields and there are cases when
+        // one of the fields may have an attribute specified, but another field
+        // would be inferred. So, for derive Error macro we default enabled
+        // to true unconditionally (i.e., even if some fields have attributes
+        // specified).
+        let default_enabled = if trait_name == "Error" {
+            true
+        } else {
+            first_match.map_or(true, |info| !info.enabled.unwrap())
+        };
+
+        let defaults = struct_meta_info.into_full(FullMetaInfo {
+            enabled: default_enabled,
+            forward: false,
+            // Default to owned true, except when first attribute has one of owned,
+            // ref or ref_mut
+            // - not a single attribute means default true
+            // - an attribute, but non of owned, ref or ref_mut means default true
+            // - an attribute, and owned, ref or ref_mut means default false
+            owned: first_match.map_or(true, |info| {
+                info.owned.is_none() && info.ref_.is_none() || info.ref_mut.is_none()
+            }),
+            ref_: false,
+            ref_mut: false,
+            info: MetaInfo::default(),
+        });
+
+        let full_meta_infos: Vec<_> = meta_infos
+            .into_iter()
+            .map(|info| info.into_full(defaults.clone()))
+            .collect();
+
+        let variant_states: Result<Vec<_>> = if derive_type == DeriveType::Enum {
+            variants
+                .iter()
+                .zip(full_meta_infos.iter().cloned())
+                .map(|(variant, info)| {
+                    State::from_variant(
+                        input,
+                        trait_name,
+                        trait_module.clone(),
+                        trait_attr.clone(),
+                        allowed_attr_params.clone(),
+                        variant,
+                        info,
+                    )
+                })
+                .collect()
+        } else {
+            Ok(vec![])
+        };
+
+        let generics = if add_type_bound {
+            add_extra_ty_param_bound(&input.generics, &trait_path)
+        } else {
+            input.generics.clone()
+        };
+
+        Ok(State {
+            input,
+            trait_name,
+            trait_ident,
+            method_ident,
+            trait_module,
+            trait_path,
+            trait_path_params: vec![],
+            trait_attr,
+            // input,
+            fields,
+            variants,
+            variant_states: variant_states?,
+            variant: None,
+            derive_type,
+            generics,
+            full_meta_infos,
+            default_info: defaults,
+        })
+    }
+
+    pub fn from_variant<'arg_input>(
+        input: &'arg_input DeriveInput,
+        trait_name: &'static str,
+        trait_module: TokenStream,
+        trait_attr: String,
+        allowed_attr_params: AttrParams,
+        variant: &'arg_input Variant,
+        default_info: FullMetaInfo,
+    ) -> Result<State<'arg_input>> {
+        let trait_name = trait_name.trim_end_matches("ToInner");
+        let trait_ident = Ident::new(trait_name, Span::call_site());
+        let method_ident = Ident::new(&trait_attr, Span::call_site());
+        let trait_path = quote!(#trait_module::#trait_ident);
+        let (derive_type, fields): (_, Vec<_>) = match variant.fields {
+            Fields::Unnamed(ref fields) => {
+                (DeriveType::Unnamed, unnamed_to_vec(fields))
+            }
+
+            Fields::Named(ref fields) => (DeriveType::Named, named_to_vec(fields)),
+            Fields::Unit => (DeriveType::Named, vec![]),
+        };
+
+        let meta_infos: Result<Vec<_>> = fields
+            .iter()
+            .map(|f| &f.attrs)
+            .map(|attrs| get_meta_info(&trait_attr, attrs, &allowed_attr_params.field))
+            .collect();
+        let meta_infos = meta_infos?;
+        let full_meta_infos: Vec<_> = meta_infos
+            .into_iter()
+            .map(|info| info.into_full(default_info.clone()))
+            .collect();
+
+        let generics = add_extra_ty_param_bound(&input.generics, &trait_path);
+
+        Ok(State {
+            input,
+            trait_name,
+            trait_module,
+            trait_path,
+            trait_path_params: vec![],
+            trait_attr,
+            trait_ident,
+            method_ident,
+            // input,
+            fields,
+            variants: vec![],
+            variant_states: vec![],
+            variant: Some(variant),
+            derive_type,
+            generics,
+            full_meta_infos,
+            default_info,
+        })
+    }
+    pub fn add_trait_path_type_param(&mut self, param: TokenStream) {
+        self.trait_path_params.push(param);
+    }
+
+    pub fn assert_single_enabled_field<'state>(
+        &'state self,
+    ) -> SingleFieldData<'input, 'state> {
+        if self.derive_type == DeriveType::Enum {
+            panic_one_field(self.trait_name, &self.trait_attr);
+        }
+        let data = self.enabled_fields_data();
+        if data.fields.len() != 1 {
+            panic_one_field(self.trait_name, &self.trait_attr);
+        };
+        SingleFieldData {
+            input_type: data.input_type,
+            field: data.fields[0],
+            field_type: data.field_types[0],
+            member: data.members[0].clone(),
+            info: data.infos[0].clone(),
+            field_ident: data.field_idents[0].clone(),
+            trait_path: data.trait_path,
+            trait_path_with_params: data.trait_path_with_params.clone(),
+            casted_trait: data.casted_traits[0].clone(),
+            impl_generics: data.impl_generics.clone(),
+            ty_generics: data.ty_generics.clone(),
+            where_clause: data.where_clause,
+            multi_field_data: data,
+        }
+    }
+
+    pub fn enabled_fields_data<'state>(&'state self) -> MultiFieldData<'input, 'state> {
+        if self.derive_type == DeriveType::Enum {
+            panic!("cannot derive({}) for enum", self.trait_name)
+        }
+        let fields = self.enabled_fields();
+        let field_idents = self.enabled_fields_idents();
+        let field_indexes = self.enabled_fields_indexes();
+        let field_types: Vec<_> = fields.iter().map(|f| &f.ty).collect();
+        let members: Vec<_> = field_idents
+            .iter()
+            .map(|ident| quote!(self.#ident))
+            .collect();
+        let trait_path = &self.trait_path;
+        let trait_path_with_params = if !self.trait_path_params.is_empty() {
+            let params = self.trait_path_params.iter();
+            quote!(#trait_path<#(#params),*>)
+        } else {
+            self.trait_path.clone()
+        };
+
+        let casted_traits: Vec<_> = field_types
+            .iter()
+            .map(|field_type| quote!(<#field_type as #trait_path_with_params>))
+            .collect();
+        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
+        let input_type = &self.input.ident;
+        let (variant_name, variant_type) = self.variant.map_or_else(
+            || (None, quote!(#input_type)),
+            |v| {
+                let variant_name = &v.ident;
+                (Some(variant_name), quote!(#input_type::#variant_name))
+            },
+        );
+        MultiFieldData {
+            input_type,
+            variant_type,
+            variant_name,
+            variant_info: self.default_info.clone(),
+            fields,
+            field_types,
+            field_indexes,
+            members,
+            infos: self.enabled_infos(),
+            field_idents,
+            method_ident: &self.method_ident,
+            trait_path,
+            trait_path_with_params,
+            casted_traits,
+            impl_generics,
+            ty_generics,
+            where_clause,
+            state: self,
+        }
+    }
+
+    pub fn enabled_variant_data<'state>(
+        &'state self,
+    ) -> MultiVariantData<'input, 'state> {
+        if self.derive_type != DeriveType::Enum {
+            panic!("can only derive({}) for enum", self.trait_name)
+        }
+        let variants = self.enabled_variants();
+        let trait_path = &self.trait_path;
+        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
+        MultiVariantData {
+            input_type: &self.input.ident,
+            variants,
+            variant_states: self.enabled_variant_states(),
+            infos: self.enabled_infos(),
+            trait_path,
+            impl_generics,
+            ty_generics,
+            where_clause,
+        }
+    }
+
+    fn enabled_variants(&self) -> Vec<&'input Variant> {
+        self.variants
+            .iter()
+            .zip(self.full_meta_infos.iter().map(|info| info.enabled))
+            .filter(|(_, ig)| *ig)
+            .map(|(v, _)| *v)
+            .collect()
+    }
+
+    fn enabled_variant_states(&self) -> Vec<&State<'input>> {
+        self.variant_states
+            .iter()
+            .zip(self.full_meta_infos.iter().map(|info| info.enabled))
+            .filter(|(_, ig)| *ig)
+            .map(|(v, _)| v)
+            .collect()
+    }
+
+    pub fn enabled_fields(&self) -> Vec<&'input Field> {
+        self.fields
+            .iter()
+            .zip(self.full_meta_infos.iter().map(|info| info.enabled))
+            .filter(|(_, ig)| *ig)
+            .map(|(f, _)| *f)
+            .collect()
+    }
+
+    fn field_idents(&self) -> Vec<TokenStream> {
+        if self.derive_type == DeriveType::Named {
+            self.fields
+                .iter()
+                .map(|f| {
+                    f.ident
+                        .as_ref()
+                        .expect("Tried to get field names of a tuple struct")
+                        .to_token_stream()
+                })
+                .collect()
+        } else {
+            let count = self.fields.len();
+            (0..count)
+                .map(|i| Index::from(i).to_token_stream())
+                .collect()
+        }
+    }
+
+    fn enabled_fields_idents(&self) -> Vec<TokenStream> {
+        self.field_idents()
+            .into_iter()
+            .zip(self.full_meta_infos.iter().map(|info| info.enabled))
+            .filter(|(_, ig)| *ig)
+            .map(|(f, _)| f)
+            .collect()
+    }
+
+    fn enabled_fields_indexes(&self) -> Vec<usize> {
+        self.full_meta_infos
+            .iter()
+            .map(|info| info.enabled)
+            .enumerate()
+            .filter(|(_, ig)| *ig)
+            .map(|(i, _)| i)
+            .collect()
+    }
+    fn enabled_infos(&self) -> Vec<FullMetaInfo> {
+        self.full_meta_infos
+            .iter()
+            .filter(|info| info.enabled)
+            .cloned()
+            .collect()
+    }
+}
+
+#[derive(Clone)]
+pub struct SingleFieldData<'input, 'state> {
+    pub input_type: &'input Ident,
+    pub field: &'input Field,
+    pub field_type: &'input Type,
+    pub field_ident: TokenStream,
+    pub member: TokenStream,
+    pub info: FullMetaInfo,
+    pub trait_path: &'state TokenStream,
+    pub trait_path_with_params: TokenStream,
+    pub casted_trait: TokenStream,
+    pub impl_generics: ImplGenerics<'state>,
+    pub ty_generics: TypeGenerics<'state>,
+    pub where_clause: Option<&'state WhereClause>,
+    multi_field_data: MultiFieldData<'input, 'state>,
+}
+
+#[derive(Clone)]
+pub struct MultiFieldData<'input, 'state> {
+    pub input_type: &'input Ident,
+    pub variant_type: TokenStream,
+    pub variant_name: Option<&'input Ident>,
+    pub variant_info: FullMetaInfo,
+    pub fields: Vec<&'input Field>,
+    pub field_types: Vec<&'input Type>,
+    pub field_idents: Vec<TokenStream>,
+    pub field_indexes: Vec<usize>,
+    pub members: Vec<TokenStream>,
+    pub infos: Vec<FullMetaInfo>,
+    pub method_ident: &'state Ident,
+    pub trait_path: &'state TokenStream,
+    pub trait_path_with_params: TokenStream,
+    pub casted_traits: Vec<TokenStream>,
+    pub impl_generics: ImplGenerics<'state>,
+    pub ty_generics: TypeGenerics<'state>,
+    pub where_clause: Option<&'state WhereClause>,
+    pub state: &'state State<'input>,
+}
+
+pub struct MultiVariantData<'input, 'state> {
+    pub input_type: &'input Ident,
+    pub variants: Vec<&'input Variant>,
+    pub variant_states: Vec<&'state State<'input>>,
+    pub infos: Vec<FullMetaInfo>,
+    pub trait_path: &'state TokenStream,
+    pub impl_generics: ImplGenerics<'state>,
+    pub ty_generics: TypeGenerics<'state>,
+    pub where_clause: Option<&'state WhereClause>,
+}
+
+impl<'input, 'state> MultiFieldData<'input, 'state> {
+    pub fn initializer<T: ToTokens>(&self, initializers: &[T]) -> TokenStream {
+        let MultiFieldData {
+            variant_type,
+            field_idents,
+            ..
+        } = self;
+        if self.state.derive_type == DeriveType::Named {
+            quote!(#variant_type{#(#field_idents: #initializers),*})
+        } else {
+            quote!(#variant_type(#(#initializers),*))
+        }
+    }
+    pub fn matcher<T: ToTokens>(
+        &self,
+        indexes: &[usize],
+        bindings: &[T],
+    ) -> TokenStream {
+        let MultiFieldData { variant_type, .. } = self;
+        let full_bindings = (0..self.state.fields.len()).map(|i| {
+            indexes.iter().position(|index| i == *index).map_or_else(
+                || quote!(_),
+                |found_index| bindings[found_index].to_token_stream(),
+            )
+        });
+        if self.state.derive_type == DeriveType::Named {
+            let field_idents = self.state.field_idents();
+            quote!(#variant_type{#(#field_idents: #full_bindings),*})
+        } else {
+            quote!(#variant_type(#(#full_bindings),*))
+        }
+    }
+}
+
+impl<'input, 'state> SingleFieldData<'input, 'state> {
+    pub fn initializer<T: ToTokens>(&self, initializers: &[T]) -> TokenStream {
+        self.multi_field_data.initializer(initializers)
+    }
+}
+
+fn get_meta_info(
+    trait_attr: &str,
+    attrs: &[Attribute],
+    allowed_attr_params: &[&str],
+) -> Result<MetaInfo> {
+    let mut it = attrs
+        .iter()
+        .filter_map(|m| m.parse_meta().ok())
+        .filter(|m| {
+            m.path()
+                .segments
+                .first()
+                .map(|p| p.ident == trait_attr)
+                .unwrap_or_default()
+        });
+
+    let mut info = MetaInfo::default();
+
+    let meta = if let Some(meta) = it.next() {
+        meta
+    } else {
+        return Ok(info);
+    };
+
+    if allowed_attr_params.is_empty() {
+        return Err(Error::new(meta.span(), "Attribute is not allowed here"));
+    }
+
+    info.enabled = Some(true);
+
+    if let Some(another_meta) = it.next() {
+        return Err(Error::new(
+            another_meta.span(),
+            "Only a single attribute is allowed",
+        ));
+    }
+
+    let list = match meta.clone() {
+        Meta::Path(_) => {
+            if allowed_attr_params.contains(&"ignore") {
+                return Ok(info);
+            } else {
+                return Err(Error::new(
+                    meta.span(),
+                    format!(
+                        "Empty attribute is not allowed, add one of the following parameters: {}",
+                        allowed_attr_params.join(", "),
+                    ),
+                ));
+            }
+        }
+        Meta::List(list) => list,
+        Meta::NameValue(val) => {
+            return Err(Error::new(
+                val.span(),
+                "Attribute doesn't support name-value format here",
+            ));
+        }
+    };
+
+    parse_punctuated_nested_meta(&mut info, &list.nested, allowed_attr_params, None)?;
+
+    Ok(info)
+}
+
+fn parse_punctuated_nested_meta(
+    info: &mut MetaInfo,
+    meta: &Punctuated<NestedMeta, Token![,]>,
+    allowed_attr_params: &[&str],
+    wrapper_name: Option<&str>,
+) -> Result<()> {
+    for meta in meta.iter() {
+        let meta = match meta {
+            NestedMeta::Meta(meta) => meta,
+            NestedMeta::Lit(lit) => {
+                return Err(Error::new(
+                    lit.span(),
+                    "Attribute doesn't support literals here",
+                ))
+            }
+        };
+
+        match meta {
+            Meta::List(list) if list.path.is_ident("not") => {
+                if wrapper_name.is_some() {
+                    // Only single top-level `not` attribute is allowed.
+                    return Err(Error::new(
+                        list.span(),
+                        "Attribute doesn't support multiple multiple or nested `not` parameters",
+                    ));
+                }
+                parse_punctuated_nested_meta(
+                    info,
+                    &list.nested,
+                    allowed_attr_params,
+                    Some("not"),
+                )?;
+            }
+
+            Meta::List(list) => {
+                let path = &list.path;
+                if !allowed_attr_params.iter().any(|param| path.is_ident(param)) {
+                    return Err(Error::new(
+                        meta.span(),
+                        format!(
+                            "Attribute nested parameter not supported. \
+                             Supported attribute parameters are: {}",
+                            allowed_attr_params.join(", "),
+                        ),
+                    ));
+                }
+
+                let mut parse_nested = true;
+
+                let attr_name = path.get_ident().unwrap().to_string();
+                match (wrapper_name, attr_name.as_str()) {
+                    (None, "owned") => info.owned = Some(true),
+                    (None, "ref") => info.ref_ = Some(true),
+                    (None, "ref_mut") => info.ref_mut = Some(true),
+
+                    #[cfg(any(feature = "from", feature = "into"))]
+                    (None, "types")
+                    | (Some("owned"), "types")
+                    | (Some("ref"), "types")
+                    | (Some("ref_mut"), "types") => {
+                        parse_nested = false;
+                        for meta in &list.nested {
+                            let typ: syn::Type = match meta {
+                                NestedMeta::Meta(meta) => {
+                                    let path = if let Meta::Path(p) = meta {
+                                        p
+                                    } else {
+                                        return Err(Error::new(
+                                            meta.span(),
+                                            format!(
+                                                "Attribute doesn't support type {}",
+                                                quote! { #meta },
+                                            ),
+                                        ));
+                                    };
+                                    syn::TypePath {
+                                        qself: None,
+                                        path: path.clone(),
+                                    }
+                                    .into()
+                                }
+                                NestedMeta::Lit(syn::Lit::Str(s)) => s.parse()?,
+                                NestedMeta::Lit(lit) => return Err(Error::new(
+                                    lit.span(),
+                                    "Attribute doesn't support nested literals here",
+                                )),
+                            };
+
+                            for ref_type in wrapper_name
+                                .map(|n| vec![RefType::from_attr_name(n)])
+                                .unwrap_or_else(|| {
+                                    vec![RefType::No, RefType::Ref, RefType::Mut]
+                                })
+                            {
+                                if info
+                                    .types
+                                    .entry(ref_type)
+                                    .or_default()
+                                    .replace(typ.clone())
+                                    .is_some()
+                                {
+                                    return Err(Error::new(
+                                        typ.span(),
+                                        format!(
+                                            "Duplicate type `{}` specified",
+                                            quote! { #path },
+                                        ),
+                                    ));
+                                }
+                            }
+                        }
+                    }
+
+                    _ => {
+                        return Err(Error::new(
+                            list.span(),
+                            format!(
+                                "Attribute doesn't support nested parameter `{}` here",
+                                quote! { #path },
+                            ),
+                        ))
+                    }
+                };
+
+                if parse_nested {
+                    parse_punctuated_nested_meta(
+                        info,
+                        &list.nested,
+                        allowed_attr_params,
+                        Some(&attr_name),
+                    )?;
+                }
+            }
+
+            Meta::Path(path) => {
+                if !allowed_attr_params.iter().any(|param| path.is_ident(param)) {
+                    return Err(Error::new(
+                        meta.span(),
+                        format!(
+                            "Attribute parameter not supported. \
+                             Supported attribute parameters are: {}",
+                            allowed_attr_params.join(", "),
+                        ),
+                    ));
+                }
+
+                let attr_name = path.get_ident().unwrap().to_string();
+                match (wrapper_name, attr_name.as_str()) {
+                    (None, "ignore") => info.enabled = Some(false),
+                    (None, "forward") => info.forward = Some(true),
+                    (Some("not"), "forward") => info.forward = Some(false),
+                    (None, "owned") => info.owned = Some(true),
+                    (None, "ref") => info.ref_ = Some(true),
+                    (None, "ref_mut") => info.ref_mut = Some(true),
+                    (None, "source") => info.source = Some(true),
+                    (Some("not"), "source") => info.source = Some(false),
+                    (None, "backtrace") => info.backtrace = Some(true),
+                    (Some("not"), "backtrace") => info.backtrace = Some(false),
+                    _ => {
+                        return Err(Error::new(
+                            path.span(),
+                            format!(
+                                "Attribute doesn't support parameter `{}` here",
+                                quote! { #path }
+                            ),
+                        ))
+                    }
+                }
+            }
+
+            Meta::NameValue(val) => {
+                return Err(Error::new(
+                    val.span(),
+                    "Attribute doesn't support name-value parameters here",
+                ))
+            }
+        }
+    }
+
+    Ok(())
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct FullMetaInfo {
+    pub enabled: bool,
+    pub forward: bool,
+    pub owned: bool,
+    pub ref_: bool,
+    pub ref_mut: bool,
+    pub info: MetaInfo,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MetaInfo {
+    pub enabled: Option<bool>,
+    pub forward: Option<bool>,
+    pub owned: Option<bool>,
+    pub ref_: Option<bool>,
+    pub ref_mut: Option<bool>,
+    pub source: Option<bool>,
+    pub backtrace: Option<bool>,
+    #[cfg(any(feature = "from", feature = "into"))]
+    pub types: HashMap<RefType, HashSet<syn::Type>>,
+}
+
+impl MetaInfo {
+    fn into_full(self, defaults: FullMetaInfo) -> FullMetaInfo {
+        FullMetaInfo {
+            enabled: self.enabled.unwrap_or(defaults.enabled),
+            forward: self.forward.unwrap_or(defaults.forward),
+            owned: self.owned.unwrap_or(defaults.owned),
+            ref_: self.ref_.unwrap_or(defaults.ref_),
+            ref_mut: self.ref_mut.unwrap_or(defaults.ref_mut),
+            info: self,
+        }
+    }
+}
+
+impl FullMetaInfo {
+    pub fn ref_types(&self) -> Vec<RefType> {
+        let mut ref_types = vec![];
+        if self.owned {
+            ref_types.push(RefType::No);
+        }
+        if self.ref_ {
+            ref_types.push(RefType::Ref);
+        }
+        if self.ref_mut {
+            ref_types.push(RefType::Mut);
+        }
+        ref_types
+    }
+
+    #[cfg(any(feature = "from", feature = "into"))]
+    pub fn additional_types(&self, ref_type: RefType) -> HashSet<syn::Type> {
+        self.info.types.get(&ref_type).cloned().unwrap_or_default()
+    }
+}
+
+pub fn get_if_type_parameter_used_in_type(
+    type_parameters: &HashSet<syn::Ident>,
+    ty: &syn::Type,
+) -> Option<syn::Type> {
+    if is_type_parameter_used_in_type(type_parameters, ty) {
+        match ty {
+            syn::Type::Reference(syn::TypeReference { elem: ty, .. }) => {
+                Some((&**ty).clone())
+            }
+            ty => Some(ty.clone()),
+        }
+    } else {
+        None
+    }
+}
+
+pub fn is_type_parameter_used_in_type(
+    type_parameters: &HashSet<syn::Ident>,
+    ty: &syn::Type,
+) -> bool {
+    match ty {
+        syn::Type::Path(ty) => {
+            if let Some(qself) = &ty.qself {
+                if is_type_parameter_used_in_type(type_parameters, &qself.ty) {
+                    return true;
+                }
+            }
+
+            if let Some(segment) = ty.path.segments.first() {
+                if type_parameters.contains(&segment.ident) {
+                    return true;
+                }
+            }
+
+            ty.path.segments.iter().any(|segment| {
+                if let syn::PathArguments::AngleBracketed(arguments) =
+                    &segment.arguments
+                {
+                    arguments.args.iter().any(|argument| match argument {
+                        syn::GenericArgument::Type(ty) => {
+                            is_type_parameter_used_in_type(type_parameters, ty)
+                        }
+                        syn::GenericArgument::Constraint(constraint) => {
+                            type_parameters.contains(&constraint.ident)
+                        }
+                        _ => false,
+                    })
+                } else {
+                    false
+                }
+            })
+        }
+
+        syn::Type::Reference(ty) => {
+            is_type_parameter_used_in_type(type_parameters, &ty.elem)
+        }
+
+        _ => false,
+    }
+}
diff --git a/tests/add.rs b/tests/add.rs
new file mode 100644 (file)
index 0000000..f9ec5a7
--- /dev/null
@@ -0,0 +1,23 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Add)]
+struct MyInts(i32, i32);
+
+#[derive(Add)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(Add)]
+enum MixedInts {
+    SmallInt(i32),
+    BigInt(i64),
+    TwoSmallInts(i32, i32),
+    NamedSmallInts { x: i32, y: i32 },
+    UnsignedOne(u32),
+    UnsignedTwo(u32),
+    Unit,
+}
diff --git a/tests/add_assign.rs b/tests/add_assign.rs
new file mode 100644 (file)
index 0000000..3268178
--- /dev/null
@@ -0,0 +1,12 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(AddAssign)]
+struct MyInts(i32, i32);
+
+#[derive(AddAssign)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
diff --git a/tests/as_mut.rs b/tests/as_mut.rs
new file mode 100644 (file)
index 0000000..e074f8b
--- /dev/null
@@ -0,0 +1,108 @@
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate derive_more;
+
+use std::path::PathBuf;
+use std::ptr;
+
+#[derive(AsMut)]
+struct SingleFieldTuple(String);
+
+#[test]
+fn single_field_tuple() {
+    let mut item = SingleFieldTuple(String::from("test"));
+
+    assert!(ptr::eq(&mut item.0, item.as_mut()));
+}
+
+#[derive(AsMut)]
+#[as_mut(forward)]
+struct SingleFieldForward(Vec<i32>);
+
+#[test]
+fn single_field_forward() {
+    let mut item = SingleFieldForward(vec![]);
+    let _: &mut [i32] = (&mut item).as_mut();
+}
+
+#[derive(AsMut)]
+struct SingleFieldStruct {
+    first: String,
+}
+
+#[test]
+fn single_field_struct() {
+    let mut item = SingleFieldStruct {
+        first: String::from("test"),
+    };
+
+    assert!(ptr::eq(&mut item.first, item.as_mut()));
+}
+
+#[derive(AsMut)]
+struct MultiFieldTuple(#[as_mut] String, #[as_mut] PathBuf, Vec<usize>);
+
+#[test]
+fn multi_field_tuple() {
+    let mut item = MultiFieldTuple(String::from("test"), PathBuf::new(), vec![]);
+
+    assert!(ptr::eq(&mut item.0, item.as_mut()));
+    assert!(ptr::eq(&mut item.1, item.as_mut()));
+}
+
+#[derive(AsMut)]
+struct MultiFieldStruct {
+    #[as_mut]
+    first: String,
+    #[as_mut]
+    second: PathBuf,
+    third: Vec<usize>,
+}
+
+#[test]
+fn multi_field_struct() {
+    let mut item = MultiFieldStruct {
+        first: String::from("test"),
+        second: PathBuf::new(),
+        third: vec![],
+    };
+
+    assert!(ptr::eq(&mut item.first, item.as_mut()));
+    assert!(ptr::eq(&mut item.second, item.as_mut()));
+}
+
+#[derive(AsMut)]
+struct SingleFieldGenericStruct<T> {
+    first: T,
+}
+
+#[test]
+fn single_field_generic_struct() {
+    let mut item = SingleFieldGenericStruct {
+        first: String::from("test"),
+    };
+
+    assert!(ptr::eq(&mut item.first, item.as_mut()));
+}
+
+#[derive(AsMut)]
+struct MultiFieldGenericStruct<T> {
+    #[as_mut]
+    first: Vec<T>,
+    #[as_mut]
+    second: PathBuf,
+    third: Vec<usize>,
+}
+
+#[test]
+fn multi_field_generic_struct() {
+    let mut item = MultiFieldGenericStruct {
+        first: b"test".to_vec(),
+        second: PathBuf::new(),
+        third: vec![],
+    };
+
+    assert!(ptr::eq(&mut item.first, item.as_mut()));
+    assert!(ptr::eq(&mut item.second, item.as_mut()));
+}
diff --git a/tests/as_ref.rs b/tests/as_ref.rs
new file mode 100644 (file)
index 0000000..1e355d2
--- /dev/null
@@ -0,0 +1,108 @@
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate derive_more;
+
+use std::path::PathBuf;
+use std::ptr;
+
+#[derive(AsRef)]
+struct SingleFieldTuple(String);
+
+#[test]
+fn single_field_tuple() {
+    let item = SingleFieldTuple(String::from("test"));
+
+    assert!(ptr::eq(&item.0, item.as_ref()));
+}
+
+#[derive(AsRef)]
+#[as_ref(forward)]
+struct SingleFieldForward(Vec<i32>);
+
+#[test]
+fn single_field_forward() {
+    let item = SingleFieldForward(vec![]);
+    let _: &[i32] = (&item).as_ref();
+}
+
+#[derive(AsRef)]
+struct SingleFieldStruct {
+    first: String,
+}
+
+#[test]
+fn single_field_struct() {
+    let item = SingleFieldStruct {
+        first: String::from("test"),
+    };
+
+    assert!(ptr::eq(&item.first, item.as_ref()));
+}
+
+#[derive(AsRef)]
+struct MultiFieldTuple(#[as_ref] String, #[as_ref] PathBuf, Vec<usize>);
+
+#[test]
+fn multi_field_tuple() {
+    let item = MultiFieldTuple(String::from("test"), PathBuf::new(), vec![]);
+
+    assert!(ptr::eq(&item.0, item.as_ref()));
+    assert!(ptr::eq(&item.1, item.as_ref()));
+}
+
+#[derive(AsRef)]
+struct MultiFieldStruct {
+    #[as_ref]
+    first: String,
+    #[as_ref]
+    second: PathBuf,
+    third: Vec<usize>,
+}
+
+#[test]
+fn multi_field_struct() {
+    let item = MultiFieldStruct {
+        first: String::from("test"),
+        second: PathBuf::new(),
+        third: vec![],
+    };
+
+    assert!(ptr::eq(&item.first, item.as_ref()));
+    assert!(ptr::eq(&item.second, item.as_ref()));
+}
+
+#[derive(AsRef)]
+struct SingleFieldGenericStruct<T> {
+    first: T,
+}
+
+#[test]
+fn single_field_generic_struct() {
+    let item = SingleFieldGenericStruct {
+        first: String::from("test"),
+    };
+
+    assert!(ptr::eq(&item.first, item.as_ref()));
+}
+
+#[derive(AsRef)]
+struct MultiFieldGenericStruct<T, U> {
+    #[as_ref]
+    first: Vec<T>,
+    #[as_ref]
+    second: [U; 2],
+    third: Vec<usize>,
+}
+
+#[test]
+fn multi_field_generic_struct() {
+    let item = MultiFieldGenericStruct {
+        first: b"test".to_vec(),
+        second: [0i32, 1i32],
+        third: vec![],
+    };
+
+    assert!(ptr::eq(&item.first, item.as_ref()));
+    assert!(ptr::eq(&item.second, item.as_ref()));
+}
diff --git a/tests/boats_display_derive.rs b/tests/boats_display_derive.rs
new file mode 100644 (file)
index 0000000..b64f75d
--- /dev/null
@@ -0,0 +1,57 @@
+// The following code is from https://github.com/withoutboats/display_derive/blob/232a32ee19e262aacbd2c93be5b4ce9e89a5fc30/tests/tests.rs
+// Written by without boats originally
+
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Display)]
+#[display(fmt = "An error has occurred.")]
+struct UnitError;
+
+#[test]
+fn unit_struct() {
+    let s = format!("{}", UnitError);
+    assert_eq!(&s[..], "An error has occurred.");
+}
+
+#[derive(Display)]
+#[display(fmt = "Error code: {}", code)]
+struct RecordError {
+    code: u32,
+}
+
+#[test]
+fn record_struct() {
+    let s = format!("{}", RecordError { code: 0 });
+    assert_eq!(&s[..], "Error code: 0");
+}
+
+#[derive(Display)]
+#[display(fmt = "Error code: {}", _0)]
+struct TupleError(i32);
+
+#[test]
+fn tuple_struct() {
+    let s = format!("{}", TupleError(2));
+    assert_eq!(&s[..], "Error code: 2");
+}
+
+#[derive(Display)]
+enum EnumError {
+    #[display(fmt = "Error code: {}", code)]
+    StructVariant { code: i32 },
+    #[display(fmt = "Error: {}", _0)]
+    TupleVariant(&'static str),
+    #[display(fmt = "An error has occurred.")]
+    UnitVariant,
+}
+
+#[test]
+fn enum_error() {
+    let s = format!("{}", EnumError::StructVariant { code: 2 });
+    assert_eq!(&s[..], "Error code: 2");
+    let s = format!("{}", EnumError::TupleVariant("foobar"));
+    assert_eq!(&s[..], "Error: foobar");
+    let s = format!("{}", EnumError::UnitVariant);
+    assert_eq!(&s[..], "An error has occurred.");
+}
diff --git a/tests/constructor.rs b/tests/constructor.rs
new file mode 100644 (file)
index 0000000..07fddb8
--- /dev/null
@@ -0,0 +1,21 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Constructor)]
+struct EmptyTuple();
+
+#[derive(Constructor)]
+struct EmptyStruct {}
+
+#[derive(Constructor)]
+struct EmptyUnit;
+
+#[derive(Constructor)]
+struct MyInts(i32, i32);
+
+#[derive(Constructor)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
diff --git a/tests/deref.rs b/tests/deref.rs
new file mode 100644 (file)
index 0000000..8227f88
--- /dev/null
@@ -0,0 +1,68 @@
+#![allow(dead_code, unused_imports)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Deref)]
+#[deref(forward)]
+struct MyBoxedInt(Box<i32>);
+
+#[derive(Deref)]
+#[deref(forward)]
+struct NumRef<'a> {
+    num: &'a i32,
+}
+
+#[derive(Deref)]
+struct NumRef2<'a> {
+    #[deref(forward)]
+    num: &'a i32,
+    useless: bool,
+}
+
+#[derive(Deref)]
+#[deref(forward)]
+struct NumRef3<'a> {
+    num: &'a i32,
+    #[deref(ignore)]
+    useless: bool,
+}
+
+#[derive(Deref)]
+struct MyInt(i32);
+
+#[derive(Deref)]
+struct Point1D {
+    x: i32,
+}
+
+#[derive(Deref)]
+struct Point1D2 {
+    x: i32,
+    #[deref(ignore)]
+    useless: bool,
+}
+
+#[derive(Deref)]
+struct CoolVec {
+    cool: bool,
+    #[deref]
+    vec: Vec<i32>,
+}
+
+#[derive(Deref)]
+struct GenericVec<T>(Vec<T>);
+
+#[test]
+fn deref_generic() {
+    let gv = GenericVec(Vec::<i32>::new());
+    assert!(gv.is_empty())
+}
+
+#[derive(Deref)]
+struct GenericBox<T>(#[deref(forward)] Box<T>);
+
+#[test]
+fn deref_generic_forward() {
+    let boxed = GenericBox(Box::new(1i32));
+    assert_eq!(*boxed, 1i32);
+}
diff --git a/tests/deref_mut.rs b/tests/deref_mut.rs
new file mode 100644 (file)
index 0000000..51a95e4
--- /dev/null
@@ -0,0 +1,124 @@
+#![allow(dead_code, unused_imports)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(DerefMut)]
+#[deref_mut(forward)]
+struct MyBoxedInt(Box<i32>);
+// Deref implementation is needed for DerefMut
+impl ::core::ops::Deref for MyBoxedInt {
+    type Target = <Box<i32> as ::core::ops::Deref>::Target;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        <Box<i32> as ::core::ops::Deref>::deref(&self.0)
+    }
+}
+
+#[derive(DerefMut)]
+struct NumRef<'a> {
+    #[deref_mut(forward)]
+    num: &'a mut i32,
+}
+// Deref implementation is needed for DerefMut
+impl<'a> ::core::ops::Deref for NumRef<'a> {
+    type Target = <&'a mut i32 as ::core::ops::Deref>::Target;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        <&'a mut i32 as ::core::ops::Deref>::deref(&self.num)
+    }
+}
+
+#[derive(DerefMut)]
+#[deref_mut(forward)]
+struct NumRef2<'a> {
+    num: &'a mut i32,
+    #[deref_mut(ignore)]
+    useless: bool,
+}
+
+// Deref implementation is needed for DerefMut
+impl<'a> ::core::ops::Deref for NumRef2<'a> {
+    type Target = <&'a mut i32 as ::core::ops::Deref>::Target;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        <&'a mut i32 as ::core::ops::Deref>::deref(&self.num)
+    }
+}
+
+#[derive(DerefMut)]
+struct MyInt(i32);
+
+// Deref implementation is needed for DerefMutToInner
+impl ::core::ops::Deref for MyInt {
+    type Target = i32;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+#[derive(DerefMut)]
+struct Point1D {
+    x: i32,
+}
+
+// Deref implementation is needed for DerefMutToInner
+impl ::core::ops::Deref for Point1D {
+    type Target = i32;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        &self.x
+    }
+}
+
+#[derive(DerefMut)]
+struct CoolVec {
+    cool: bool,
+    #[deref_mut]
+    vec: Vec<i32>,
+}
+impl ::core::ops::Deref for CoolVec {
+    type Target = Vec<i32>;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        &self.vec
+    }
+}
+
+#[derive(DerefMut)]
+struct GenericVec<T>(Vec<T>);
+
+impl<T> ::core::ops::Deref for GenericVec<T> {
+    type Target = Vec<T>;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+#[test]
+fn deref_mut_generic() {
+    let mut gv = GenericVec::<i32>(vec![42]);
+    assert!(gv.get_mut(0).is_some());
+}
+
+#[derive(DerefMut)]
+struct GenericBox<T>(#[deref_mut(forward)] Box<T>);
+
+impl<T> ::core::ops::Deref for GenericBox<T>
+where
+    Box<T>: ::core::ops::Deref,
+{
+    type Target = <Box<T> as ::core::ops::Deref>::Target;
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        <Box<T> as ::core::ops::Deref>::deref(&self.0)
+    }
+}
+
+#[test]
+fn deref_mut_generic_forward() {
+    let mut boxed = GenericBox(Box::new(1i32));
+    *boxed = 3;
+    assert_eq!(*boxed, 3i32);
+}
diff --git a/tests/display.rs b/tests/display.rs
new file mode 100644 (file)
index 0000000..fae596b
--- /dev/null
@@ -0,0 +1,429 @@
+#![allow(dead_code, unused_imports)]
+#[macro_use]
+extern crate derive_more;
+
+use std::path::PathBuf;
+
+// Here just to make sure that this doesn't conflict with
+// the derives in some way
+use std::fmt::Binary;
+
+#[derive(Display, Octal, Binary)]
+struct MyInt(i32);
+
+#[derive(UpperHex)]
+enum IntEnum {
+    U8(u8),
+    I8(i8),
+}
+
+#[derive(Display)]
+#[display(fmt = "({}, {})", x, y)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(Display)]
+#[display(fmt = "{}", "self.sign()")]
+struct PositiveOrNegative {
+    x: i32,
+}
+
+impl PositiveOrNegative {
+    fn sign(&self) -> &str {
+        if self.x >= 0 {
+            "Positive"
+        } else {
+            "Negative"
+        }
+    }
+}
+
+#[derive(Display)]
+#[display(fmt = "{}", message)]
+struct Error {
+    message: &'static str,
+    backtrace: (),
+}
+
+impl Error {
+    fn new(message: &'static str) -> Self {
+        Self {
+            message,
+            backtrace: (),
+        }
+    }
+}
+
+#[derive(Display)]
+enum E {
+    Uint(u32),
+    #[display(fmt = "I am B {:b}", i)]
+    Binary {
+        i: i8,
+    },
+    #[display(fmt = "I am C {}", "_0.display()")]
+    Path(PathBuf),
+}
+
+#[derive(Display)]
+#[display(fmt = "Java EE")]
+enum EE {
+    A,
+    B,
+}
+
+#[derive(Display)]
+#[display(fmt = "Hello there!")]
+union U {
+    i: u32,
+}
+
+#[derive(Octal)]
+#[octal(fmt = "7")]
+struct S;
+
+#[derive(UpperHex)]
+#[upper_hex(fmt = "UpperHex")]
+struct UH;
+
+#[derive(DebugCustom)]
+#[debug(fmt = "MyDebug")]
+struct D;
+
+#[derive(Display)]
+struct Unit;
+
+#[derive(Display)]
+struct UnitStruct {}
+
+#[derive(Display)]
+enum EmptyEnum {}
+
+#[derive(Display)]
+#[display(fmt = "Generic")]
+struct Generic<T>(T);
+
+#[derive(Display)]
+#[display(fmt = "Here's a prefix for {} and a suffix")]
+enum Affix {
+    A(u32),
+    #[display(fmt = "{} -- {}", wat, stuff)]
+    B {
+        wat: String,
+        stuff: bool,
+    },
+}
+
+#[derive(Debug, Display)]
+#[display(fmt = "{:?}", self)]
+struct DebugStructAsDisplay;
+
+#[test]
+fn check_display() {
+    assert_eq!(MyInt(-2).to_string(), "-2");
+    assert_eq!(format!("{:b}", MyInt(9)), "1001");
+    assert_eq!(format!("{:#b}", MyInt(9)), "0b1001");
+    assert_eq!(format!("{:o}", MyInt(9)), "11");
+    assert_eq!(format!("{:X}", IntEnum::I8(-1)), "FF");
+    assert_eq!(format!("{:#X}", IntEnum::U8(255)), "0xFF");
+    assert_eq!(Point2D { x: 3, y: 4 }.to_string(), "(3, 4)");
+    assert_eq!(PositiveOrNegative { x: 123 }.to_string(), "Positive");
+    assert_eq!(PositiveOrNegative { x: 0 }.to_string(), "Positive");
+    assert_eq!(PositiveOrNegative { x: -465 }.to_string(), "Negative");
+    assert_eq!(Error::new("Error").to_string(), "Error");
+    assert_eq!(E::Uint(2).to_string(), "2");
+    assert_eq!(E::Binary { i: -2 }.to_string(), "I am B 11111110");
+    assert_eq!(E::Path("abc".into()).to_string(), "I am C abc");
+    assert_eq!(EE::A.to_string(), "Java EE");
+    assert_eq!(EE::B.to_string(), "Java EE");
+    assert_eq!(U { i: 2 }.to_string(), "Hello there!");
+    assert_eq!(format!("{:o}", S), "7");
+    assert_eq!(format!("{:X}", UH), "UpperHex");
+    assert_eq!(format!("{:?}", D), "MyDebug");
+    assert_eq!(Unit.to_string(), "Unit");
+    assert_eq!(UnitStruct {}.to_string(), "UnitStruct");
+    assert_eq!(Generic(()).to_string(), "Generic");
+    assert_eq!(
+        Affix::A(2).to_string(),
+        "Here's a prefix for 2 and a suffix"
+    );
+    assert_eq!(
+        Affix::B {
+            wat: "things".to_owned(),
+            stuff: false,
+        }
+        .to_string(),
+        "Here's a prefix for things -- false and a suffix"
+    );
+    assert_eq!(DebugStructAsDisplay.to_string(), "DebugStructAsDisplay");
+}
+
+mod generic {
+    #[derive(Display)]
+    #[display(fmt = "Generic {}", field)]
+    struct NamedGenericStruct<T> {
+        field: T,
+    }
+    #[test]
+    fn named_generic_struct() {
+        assert_eq!(NamedGenericStruct { field: 1 }.to_string(), "Generic 1");
+    }
+
+    #[derive(Display)]
+    struct AutoNamedGenericStruct<T> {
+        field: T,
+    }
+    #[test]
+    fn auto_named_generic_struct() {
+        assert_eq!(AutoNamedGenericStruct { field: 1 }.to_string(), "1");
+    }
+
+    #[derive(Display)]
+    #[display(fmt = "Generic {}", "_0")]
+    struct UnnamedGenericStruct<T>(T);
+    #[test]
+    fn unnamed_generic_struct() {
+        assert_eq!(UnnamedGenericStruct(2).to_string(), "Generic 2");
+    }
+
+    #[derive(Display)]
+    struct AutoUnnamedGenericStruct<T>(T);
+    #[test]
+    fn auto_unnamed_generic_struct() {
+        assert_eq!(AutoUnnamedGenericStruct(2).to_string(), "2");
+    }
+
+    #[derive(Display)]
+    enum GenericEnum<A, B> {
+        #[display(fmt = "Gen::A {}", field)]
+        A { field: A },
+        #[display(fmt = "Gen::B {}", "_0")]
+        B(B),
+    }
+    #[test]
+    fn generic_enum() {
+        assert_eq!(GenericEnum::A::<_, u8> { field: 1 }.to_string(), "Gen::A 1");
+        assert_eq!(GenericEnum::B::<u8, _>(2).to_string(), "Gen::B 2");
+    }
+
+    #[derive(Display)]
+    enum AutoGenericEnum<A, B> {
+        A { field: A },
+        B(B),
+    }
+    #[test]
+    fn auto_generic_enum() {
+        assert_eq!(AutoGenericEnum::A::<_, u8> { field: 1 }.to_string(), "1");
+        assert_eq!(AutoGenericEnum::B::<u8, _>(2).to_string(), "2");
+    }
+
+    #[derive(Display)]
+    #[display(fmt = "{} {} <-> {0:o} {1:#x} <-> {0:?} {1:X?}", a, b)]
+    struct MultiTraitNamedGenericStruct<A, B> {
+        a: A,
+        b: B,
+    }
+    #[test]
+    fn multi_trait_named_generic_struct() {
+        let s = MultiTraitNamedGenericStruct { a: 8u8, b: 255 };
+        assert_eq!(s.to_string(), "8 255 <-> 10 0xff <-> 8 FF");
+    }
+
+    #[derive(Display)]
+    #[display(fmt = "{} {} {{}} {0:o} {1:#x} - {0:>4?} {1:^4X?}", "_0", "_1")]
+    struct MultiTraitUnnamedGenericStruct<A, B>(A, B);
+    #[test]
+    fn multi_trait_unnamed_generic_struct() {
+        let s = MultiTraitUnnamedGenericStruct(8u8, 255);
+        assert_eq!(s.to_string(), "8 255 {} 10 0xff -    8  FF ");
+    }
+
+    #[derive(Display)]
+    #[display(fmt = "{}", "3 * 4")]
+    struct UnusedGenericStruct<T>(T);
+    #[test]
+    fn unused_generic_struct() {
+        let s = UnusedGenericStruct(());
+        assert_eq!(s.to_string(), "12");
+    }
+
+    mod associated_type_field_enumerator {
+        use super::*;
+
+        trait Trait {
+            type Type;
+        }
+
+        struct Struct;
+
+        impl Trait for Struct {
+            type Type = i32;
+        }
+
+        #[test]
+        fn auto_generic_named_struct_associated() {
+            #[derive(Display)]
+            struct AutoGenericNamedStructAssociated<T: Trait> {
+                field: <T as Trait>::Type,
+            }
+
+            let s = AutoGenericNamedStructAssociated::<Struct> { field: 10 };
+            assert_eq!(s.to_string(), "10");
+        }
+
+        #[test]
+        fn auto_generic_unnamed_struct_associated() {
+            #[derive(Display)]
+            struct AutoGenericUnnamedStructAssociated<T: Trait>(<T as Trait>::Type);
+
+            let s = AutoGenericUnnamedStructAssociated::<Struct>(10);
+            assert_eq!(s.to_string(), "10");
+        }
+
+        #[test]
+        fn auto_generic_enum_associated() {
+            #[derive(Display)]
+            enum AutoGenericEnumAssociated<T: Trait> {
+                Enumerator(<T as Trait>::Type),
+            }
+
+            let e = AutoGenericEnumAssociated::<Struct>::Enumerator(10);
+            assert_eq!(e.to_string(), "10");
+        }
+    }
+
+    mod complex_type_field_enumerator {
+        use super::*;
+
+        #[derive(Display)]
+        struct Struct<T>(T);
+
+        #[test]
+        fn auto_generic_named_struct_complex() {
+            #[derive(Display)]
+            struct AutoGenericNamedStructComplex<T> {
+                field: Struct<T>,
+            }
+
+            let s = AutoGenericNamedStructComplex { field: Struct(10) };
+            assert_eq!(s.to_string(), "10");
+        }
+
+        #[test]
+        fn auto_generic_unnamed_struct_complex() {
+            #[derive(Display)]
+            struct AutoGenericUnnamedStructComplex<T>(Struct<T>);
+
+            let s = AutoGenericUnnamedStructComplex(Struct(10));
+            assert_eq!(s.to_string(), "10");
+        }
+
+        #[test]
+        fn auto_generic_enum_complex() {
+            #[derive(Display)]
+            enum AutoGenericEnumComplex<T> {
+                Enumerator(Struct<T>),
+            }
+
+            let e = AutoGenericEnumComplex::Enumerator(Struct(10));
+            assert_eq!(e.to_string(), "10")
+        }
+    }
+
+    mod reference {
+        use super::*;
+
+        #[test]
+        fn auto_generic_reference() {
+            #[derive(Display)]
+            struct AutoGenericReference<'a, T>(&'a T);
+
+            let s = AutoGenericReference(&10);
+            assert_eq!(s.to_string(), "10");
+        }
+
+        #[test]
+        fn auto_generic_static_reference() {
+            #[derive(Display)]
+            struct AutoGenericStaticReference<T: 'static>(&'static T);
+
+            let s = AutoGenericStaticReference(&10);
+            assert_eq!(s.to_string(), "10");
+        }
+    }
+
+    mod indirect {
+        use super::*;
+
+        #[derive(Display)]
+        struct Struct<T>(T);
+
+        #[test]
+        fn auto_generic_indirect() {
+            #[derive(Display)]
+            struct AutoGenericIndirect<T: 'static>(Struct<&'static T>);
+
+            const V: i32 = 10;
+            let s = AutoGenericIndirect(Struct(&V));
+            assert_eq!(s.to_string(), "10");
+        }
+    }
+
+    mod bound {
+        use super::*;
+
+        #[test]
+        fn simple() {
+            #[derive(Display)]
+            #[display(fmt = "{} {}", _0, _1)]
+            struct Struct<T1, T2>(T1, T2);
+
+            let s = Struct(10, 20);
+            assert_eq!(s.to_string(), "10 20");
+        }
+
+        #[test]
+        fn redundant() {
+            #[derive(Display)]
+            #[display(bound = "T1: ::core::fmt::Display, T2: ::core::fmt::Display")]
+            #[display(fmt = "{} {}", _0, _1)]
+            struct Struct<T1, T2>(T1, T2);
+
+            let s = Struct(10, 20);
+            assert_eq!(s.to_string(), "10 20");
+        }
+
+        #[test]
+        fn complex() {
+            trait Trait1 {
+                fn function1(&self) -> &'static str;
+            }
+
+            trait Trait2 {
+                fn function2(&self) -> &'static str;
+            }
+
+            impl Trait1 for i32 {
+                fn function1(&self) -> &'static str {
+                    "WHAT"
+                }
+            }
+
+            impl Trait2 for i32 {
+                fn function2(&self) -> &'static str {
+                    "EVER"
+                }
+            }
+
+            #[derive(Display)]
+            #[display(bound = "T1: Trait1 + Trait2, T2: Trait1 + Trait2")]
+            #[display(fmt = "{} {} {} {}", "_0.function1()", _0, "_1.function2()", _1)]
+            struct Struct<T1, T2>(T1, T2);
+
+            let s = Struct(10, 20);
+            assert_eq!(s.to_string(), "WHAT 10 EVER 20");
+        }
+    }
+}
diff --git a/tests/error/derives_for_enums_with_source.rs b/tests/error/derives_for_enums_with_source.rs
new file mode 100644 (file)
index 0000000..2513ab6
--- /dev/null
@@ -0,0 +1,249 @@
+use super::*;
+
+derive_display!(TestErr);
+#[derive(Debug, Error)]
+enum TestErr {
+    Unit,
+    NamedImplicitNoSource {
+        field: i32,
+    },
+    NamedImplicitSource {
+        source: SimpleErr,
+        field: i32,
+    },
+    NamedExplicitNoSource {
+        #[error(not(source))]
+        source: SimpleErr,
+        field: i32,
+    },
+    NamedExplicitSource {
+        #[error(source)]
+        explicit_source: SimpleErr,
+        field: i32,
+    },
+    NamedExplicitNoSourceRedundant {
+        #[error(not(source))]
+        field: i32,
+    },
+    NamedExplicitSourceRedundant {
+        #[error(source)]
+        source: SimpleErr,
+        field: i32,
+    },
+    NamedExplicitSuppressesImplicit {
+        source: i32,
+        #[error(source)]
+        field: SimpleErr,
+    },
+    UnnamedImplicitNoSource(i32, i32),
+    UnnamedImplicitSource(SimpleErr),
+    UnnamedExplicitNoSource(#[error(not(source))] SimpleErr),
+    UnnamedExplicitSource(#[error(source)] SimpleErr, i32),
+    UnnamedExplicitNoSourceRedundant(
+        #[error(not(source))] i32,
+        #[error(not(source))] i32,
+    ),
+    UnnamedExplicitSourceRedundant(#[error(source)] SimpleErr),
+    NamedIgnore {
+        #[error(ignore)]
+        source: SimpleErr,
+        field: i32,
+    },
+    UnnamedIgnore(#[error(ignore)] SimpleErr),
+    NamedIgnoreRedundant {
+        #[error(ignore)]
+        field: i32,
+    },
+    UnnamedIgnoreRedundant(#[error(ignore)] i32, #[error(ignore)] i32),
+    #[error(ignore)]
+    NamedVariantIgnore {
+        source: SimpleErr,
+        field: i32,
+    },
+    #[error(ignore)]
+    UnnamedVariantIgnore(SimpleErr),
+    #[error(ignore)]
+    NamedVariantIgnoreRedundant {
+        field: i32,
+    },
+    #[error(ignore)]
+    UnnamedVariantIgnoreRedundant(i32, i32),
+}
+
+#[test]
+fn unit() {
+    assert!(TestErr::Unit.source().is_none());
+}
+
+#[test]
+fn named_implicit_no_source() {
+    let err = TestErr::NamedImplicitNoSource { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_implicit_source() {
+    let err = TestErr::NamedImplicitSource {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source() {
+    let err = TestErr::NamedExplicitNoSource {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_explicit_source() {
+    let err = TestErr::NamedExplicitSource {
+        explicit_source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source_redundant() {
+    let err = TestErr::NamedExplicitNoSourceRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_explicit_source_redundant() {
+    let err = TestErr::NamedExplicitSourceRedundant {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_suppresses_implicit() {
+    let err = TestErr::NamedExplicitSuppressesImplicit {
+        source: 0,
+        field: SimpleErr,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_implicit_no_source() {
+    assert!(TestErr::UnnamedImplicitNoSource(0, 0).source().is_none());
+}
+
+#[test]
+fn unnamed_implicit_source() {
+    let err = TestErr::UnnamedImplicitSource(SimpleErr);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source() {
+    let err = TestErr::UnnamedExplicitNoSource(SimpleErr);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source() {
+    let err = TestErr::UnnamedExplicitSource(SimpleErr, 0);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source_redundant() {
+    let err = TestErr::UnnamedExplicitNoSourceRedundant(0, 0);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source_redundant() {
+    let err = TestErr::UnnamedExplicitSourceRedundant(SimpleErr);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_ignore() {
+    let err = TestErr::NamedIgnore {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_ignore() {
+    let err = TestErr::UnnamedIgnore(SimpleErr);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_ignore_redundant() {
+    let err = TestErr::NamedIgnoreRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_ignore_redundant() {
+    let err = TestErr::UnnamedIgnoreRedundant(0, 0);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_variant_ignore() {
+    let err = TestErr::NamedVariantIgnore {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_variant_ignore() {
+    let err = TestErr::UnnamedVariantIgnore(SimpleErr);
+
+    assert!(err.source().is_none())
+}
+
+#[test]
+fn named_variant_ignore_redundant() {
+    let err = TestErr::NamedVariantIgnoreRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_variant_ignore_redundant() {
+    let err = TestErr::UnnamedVariantIgnoreRedundant(0, 0);
+
+    assert!(err.source().is_none())
+}
diff --git a/tests/error/derives_for_generic_enums_with_source.rs b/tests/error/derives_for_generic_enums_with_source.rs
new file mode 100644 (file)
index 0000000..92f3b3a
--- /dev/null
@@ -0,0 +1,248 @@
+use super::*;
+
+derive_display!(TestErr, T, E);
+#[derive(Debug, Error)]
+enum TestErr<E, T> {
+    Unit,
+    NamedImplicitNoSource {
+        field: T,
+    },
+    NamedImplicitSource {
+        source: E,
+        field: T,
+    },
+    NamedExplicitNoSource {
+        #[error(not(source))]
+        source: E,
+        field: T,
+    },
+    NamedExplicitSource {
+        #[error(source)]
+        explicit_source: E,
+        field: T,
+    },
+    NamedExplicitNoSourceRedundant {
+        #[error(not(source))]
+        field: T,
+    },
+    NamedExplicitSourceRedundant {
+        #[error(source)]
+        source: E,
+        field: T,
+    },
+    NamedExplicitSuppressesImplicit {
+        source: T,
+        #[error(source)]
+        field: E,
+    },
+    UnnamedImplicitNoSource(T, T),
+    UnnamedImplicitSource(E),
+    UnnamedExplicitNoSource(#[error(not(source))] E),
+    UnnamedExplicitSource(#[error(source)] E, T),
+    UnnamedExplicitNoSourceRedundant(#[error(not(source))] T, #[error(not(source))] T),
+    UnnamedExplicitSourceRedundant(#[error(source)] E),
+    NamedIgnore {
+        #[error(ignore)]
+        source: E,
+        field: T,
+    },
+    UnnamedIgnore(#[error(ignore)] E),
+    NamedIgnoreRedundant {
+        #[error(ignore)]
+        field: T,
+    },
+    UnnamedIgnoreRedundant(#[error(ignore)] T, #[error(ignore)] T),
+    #[error(ignore)]
+    NamedVariantIgnore {
+        source: E,
+        field: T,
+    },
+    #[error(ignore)]
+    UnnamedVariantIgnore(E),
+    #[error(ignore)]
+    NamedVariantIgnoreRedundant {
+        field: T,
+    },
+    #[error(ignore)]
+    UnnamedVariantIgnoreRedundant(T, T),
+}
+
+#[test]
+fn unit() {
+    assert!(TestErr::<SimpleErr, i32>::Unit.source().is_none());
+}
+
+#[test]
+fn named_implicit_no_source() {
+    let err = TestErr::<SimpleErr, _>::NamedImplicitNoSource { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_implicit_source() {
+    let err = TestErr::NamedImplicitSource {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source() {
+    let err = TestErr::NamedExplicitNoSource {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_explicit_source() {
+    let err = TestErr::NamedExplicitSource {
+        explicit_source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source_redundant() {
+    let err = TestErr::<SimpleErr, _>::NamedExplicitNoSourceRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_explicit_source_redundant() {
+    let err = TestErr::NamedExplicitSourceRedundant {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_suppresses_implicit() {
+    let err = TestErr::NamedExplicitSuppressesImplicit {
+        source: 0,
+        field: SimpleErr,
+    };
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_implicit_no_source() {
+    let err = TestErr::<SimpleErr, _>::UnnamedImplicitNoSource(0, 0);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_implicit_source() {
+    let err = TestErr::<_, i32>::UnnamedImplicitSource(SimpleErr);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source() {
+    let err = TestErr::<_, i32>::UnnamedExplicitNoSource(SimpleErr);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source() {
+    let err = TestErr::UnnamedExplicitSource(SimpleErr, 0);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source_redundant() {
+    let err = TestErr::<SimpleErr, _>::UnnamedExplicitNoSourceRedundant(0, 0);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source_redundant() {
+    let err = TestErr::<_, i32>::UnnamedExplicitSourceRedundant(SimpleErr);
+
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_ignore() {
+    let err = TestErr::NamedIgnore {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_ignore() {
+    let err = TestErr::<_, i32>::UnnamedIgnore(SimpleErr);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_ignore_redundant() {
+    let err = TestErr::<SimpleErr, _>::NamedIgnoreRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_ignore_redundant() {
+    let err = TestErr::<SimpleErr, _>::UnnamedIgnoreRedundant(0, 0);
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_variant_ignore() {
+    let err = TestErr::NamedVariantIgnore {
+        source: SimpleErr,
+        field: 0,
+    };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_variant_ignore() {
+    let err = TestErr::<_, i32>::UnnamedVariantIgnore(SimpleErr);
+
+    assert!(err.source().is_none())
+}
+
+#[test]
+fn named_variant_ignore_redundant() {
+    let err = TestErr::<SimpleErr, _>::NamedVariantIgnoreRedundant { field: 0 };
+
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn unnamed_variant_ignore_redundant() {
+    let err = TestErr::<SimpleErr, _>::UnnamedVariantIgnoreRedundant(0, 0);
+
+    assert!(err.source().is_none())
+}
diff --git a/tests/error/derives_for_generic_structs_with_source.rs b/tests/error/derives_for_generic_structs_with_source.rs
new file mode 100644 (file)
index 0000000..248e3c9
--- /dev/null
@@ -0,0 +1,245 @@
+use super::*;
+
+#[test]
+fn named_implicit_no_source() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T> {
+        field: T,
+    }
+
+    assert!(TestErr::<i32>::default().source().is_none());
+}
+
+#[test]
+fn named_implicit_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        source: E,
+        field: T,
+    }
+
+    let err = TestErr::<SimpleErr, i32>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(not(source))]
+        source: E,
+        field: T,
+    }
+
+    let err = TestErr::<SimpleErr, i32>::default();
+    assert!(err.source().is_none());
+}
+
+#[test]
+fn named_explicit_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(source)]
+        explicit_source: E,
+        field: T,
+    }
+
+    let err = TestErr::<SimpleErr, i32>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T> {
+        #[error(not(source))]
+        field: T,
+    }
+
+    assert!(TestErr::<i32>::default().source().is_none());
+}
+
+#[test]
+fn named_explicit_source_redundant() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(source)]
+        source: E,
+        field: T,
+    }
+
+    let err = TestErr::<SimpleErr, i32>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_suppresses_implicit() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        source: E,
+        #[error(source)]
+        field: T,
+    }
+
+    let err = TestErr::<i32, SimpleErr>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_implicit_no_source() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T>(T, T);
+
+    assert!(TestErr::<i32>::default().source().is_none());
+}
+
+#[test]
+fn unnamed_implicit_source() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E>(E);
+
+    let err = TestErr::<SimpleErr>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E>(#[error(not(source))] E);
+
+    assert!(TestErr::<SimpleErr>::default().source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T>(#[error(source)] E, T);
+
+    let err = TestErr::<SimpleErr, i32>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T>(#[error(not(source))] T, #[error(not(source))] T);
+
+    assert!(TestErr::<i32>::default().source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source_redundant() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E>(#[error(source)] E);
+
+    let err = TestErr::<SimpleErr>::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_ignore() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E, T> {
+        #[error(ignore)]
+        source: E,
+        field: T,
+    }
+
+    assert!(TestErr::<SimpleErr, i32>::default().source().is_none());
+}
+
+#[test]
+fn unnamed_ignore() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<E>(#[error(ignore)] E);
+
+    assert!(TestErr::<SimpleErr>::default().source().is_none());
+}
+
+#[test]
+fn named_ignore_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T> {
+        #[error(ignore)]
+        field: T,
+    }
+
+    assert!(TestErr::<i32>::default().source().is_none());
+}
+
+#[test]
+fn unnamed_ignore_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T>(#[error(ignore)] T, #[error(ignore)] T);
+
+    assert!(TestErr::<i32>::default().source().is_none());
+}
+
+#[test]
+fn named_struct_ignore() {
+    derive_display!(TestErr, E, T);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr<E, T> {
+        source: E,
+        field: T,
+    }
+
+    assert!(TestErr::<SimpleErr, i32>::default().source().is_none())
+}
+
+#[test]
+fn unnamed_struct_ignore() {
+    derive_display!(TestErr, E);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr<E>(E);
+
+    assert!(TestErr::<SimpleErr>::default().source().is_none())
+}
+
+#[test]
+fn named_struct_ignore_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr<T> {
+        field: T,
+    }
+
+    assert!(TestErr::<i32>::default().source().is_none())
+}
+
+#[test]
+fn unnamed_struct_ignore_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr<T>(T, T);
+
+    assert!(TestErr::<i32>::default().source().is_none())
+}
diff --git a/tests/error/derives_for_structs_with_source.rs b/tests/error/derives_for_structs_with_source.rs
new file mode 100644 (file)
index 0000000..d46bd5a
--- /dev/null
@@ -0,0 +1,249 @@
+use super::*;
+
+#[test]
+fn unit() {
+    assert!(SimpleErr.source().is_none());
+}
+
+#[test]
+fn named_implicit_no_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn named_implicit_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        source: SimpleErr,
+        field: i32,
+    }
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(not(source))]
+        source: SimpleErr,
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn named_explicit_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(source)]
+        explicit_source: SimpleErr,
+        field: i32,
+    }
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_no_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(not(source))]
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn named_explicit_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(source)]
+        source: SimpleErr,
+        field: i32,
+    }
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_explicit_suppresses_implicit() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        source: i32,
+        #[error(source)]
+        field: SimpleErr,
+    }
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_implicit_no_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(i32, i32);
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn unnamed_implicit_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(SimpleErr);
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(not(source))] SimpleErr);
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(source)] SimpleErr, i32);
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn unnamed_explicit_no_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(not(source))] i32, #[error(not(source))] i32);
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn unnamed_explicit_source_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(source)] SimpleErr);
+
+    let err = TestErr::default();
+    assert!(err.source().is_some());
+    assert!(err.source().unwrap().is::<SimpleErr>());
+}
+
+#[test]
+fn named_ignore() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(ignore)]
+        source: SimpleErr,
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn unnamed_ignore() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(ignore)] SimpleErr);
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn named_ignore_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        #[error(ignore)]
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn unnamed_ignore_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(#[error(ignore)] i32, #[error(ignore)] i32);
+
+    assert!(TestErr::default().source().is_none());
+}
+
+#[test]
+fn named_struct_ignore() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr {
+        source: SimpleErr,
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none())
+}
+
+#[test]
+fn unnamed_struct_ignore() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr(SimpleErr);
+
+    assert!(TestErr::default().source().is_none())
+}
+
+#[test]
+fn named_struct_ignore_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr {
+        field: i32,
+    }
+
+    assert!(TestErr::default().source().is_none())
+}
+
+#[test]
+fn unnamed_struct_ignore_redundant() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    #[error(ignore)]
+    struct TestErr(i32, i32);
+
+    assert!(TestErr::default().source().is_none())
+}
diff --git a/tests/error/mod.rs b/tests/error/mod.rs
new file mode 100644 (file)
index 0000000..5d7190d
--- /dev/null
@@ -0,0 +1,56 @@
+use std::error::Error;
+
+/// Derives `std::fmt::Display` for structs/enums.
+/// Derived implementation outputs empty string.
+/// Useful, as a way to formally satisfy `Display` trait bound.
+///
+/// ## Syntax:
+///
+/// For regular structs/enums:
+///
+/// ```
+/// enum MyEnum {
+///     ...
+/// }
+///
+/// derive_display!(MyEnum);
+/// ```
+///
+/// For generic structs/enums:
+///
+/// ```
+/// struct MyGenericStruct<T, U> {
+///     ...
+/// }
+///
+/// derive_display!(MyGenericStruct, T, U);
+/// ```
+macro_rules! derive_display {
+    (@fmt) => {
+        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+            write!(f, "")
+        }
+    };
+    ($type:ident) => {
+        impl ::std::fmt::Display for $type {
+            derive_display!(@fmt);
+        }
+    };
+    ($type:ident, $($type_parameters:ident),*) => {
+        impl<$($type_parameters),*> ::std::fmt::Display for $type<$($type_parameters),*> {
+            derive_display!(@fmt);
+        }
+    };
+}
+
+mod derives_for_enums_with_source;
+mod derives_for_generic_enums_with_source;
+mod derives_for_generic_structs_with_source;
+mod derives_for_structs_with_source;
+
+#[cfg(feature = "nightly")]
+mod nightly;
+
+derive_display!(SimpleErr);
+#[derive(Default, Debug, Error)]
+struct SimpleErr;
diff --git a/tests/error/nightly/derives_for_enums_with_backtrace.rs b/tests/error/nightly/derives_for_enums_with_backtrace.rs
new file mode 100644 (file)
index 0000000..c4ff481
--- /dev/null
@@ -0,0 +1,272 @@
+use super::*;
+
+derive_display!(TestErr);
+#[derive(Debug, Error)]
+enum TestErr {
+    Unit,
+    NamedImplicitNoBacktrace {
+        field: i32,
+    },
+    NamedImplicitBacktraceByFieldName {
+        backtrace: MyBacktrace,
+        field: i32,
+    },
+    NamedImplicitBacktraceByFieldType {
+        implicit_backtrace: Backtrace,
+        field: i32,
+    },
+    NamedExplicitNoBacktraceByFieldName {
+        #[error(not(backtrace))]
+        backtrace: MyBacktrace,
+        field: i32,
+    },
+    NamedExplicitNoBacktraceByFieldType {
+        #[error(not(backtrace))]
+        implicit_backtrace: Backtrace,
+        field: i32,
+    },
+    NamedExplicitBacktrace {
+        #[error(backtrace)]
+        explicit_backtrace: MyBacktrace,
+        field: i32,
+    },
+    NamedExplicitNoBacktraceRedundant {
+        #[error(not(backtrace))]
+        not_backtrace: MyBacktrace,
+        #[error(not(backtrace))]
+        field: i32,
+    },
+    NamedExplicitBacktraceByFieldNameRedundant {
+        #[error(backtrace)]
+        backtrace: MyBacktrace,
+        field: i32,
+    },
+    NamedExplicitBacktraceByFieldTypeRedundant {
+        #[error(backtrace)]
+        implicit_backtrace: Backtrace,
+        field: i32,
+    },
+    NamedExplicitSupressesImplicit {
+        #[error(backtrace)]
+        not_backtrace: MyBacktrace,
+        backtrace: Backtrace,
+        field: i32,
+    },
+    UnnamedImplicitNoBacktrace(i32, i32),
+    UnnamedImplicitBacktrace(Backtrace, i32, i32),
+    UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, i32),
+    UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, i32, i32),
+    UnnamedExplicitNoBacktraceRedundant(
+        #[error(not(backtrace))] MyBacktrace,
+        #[error(not(backtrace))] i32,
+    ),
+    UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, i32, i32),
+    UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, i32),
+}
+
+impl TestErr {
+    fn get_stored_backtrace(&self) -> &Backtrace {
+        match self {
+            Self::NamedImplicitBacktraceByFieldName { backtrace, .. } => backtrace,
+            Self::NamedImplicitBacktraceByFieldType {
+                implicit_backtrace, ..
+            } => implicit_backtrace,
+            Self::NamedExplicitBacktrace {
+                explicit_backtrace, ..
+            } => explicit_backtrace,
+            Self::NamedExplicitBacktraceByFieldNameRedundant { backtrace, .. } => {
+                backtrace
+            }
+            Self::NamedExplicitBacktraceByFieldTypeRedundant {
+                implicit_backtrace,
+                ..
+            } => implicit_backtrace,
+            Self::NamedExplicitSupressesImplicit { not_backtrace, .. } => not_backtrace,
+            Self::UnnamedImplicitBacktrace(backtrace, _, _) => backtrace,
+            Self::UnnamedExplicitBacktrace(backtrace, _, _) => backtrace,
+            Self::UnnamedExplicitBacktraceRedundant(backtrace, _, _) => backtrace,
+            Self::UnnamedExplicitSupressesImplicit(backtrace, _, _) => backtrace,
+            _ => panic!("ERROR IN TEST IMPLEMENTATION"),
+        }
+    }
+
+    fn get_unused_backtrace(&self) -> &Backtrace {
+        match self {
+            Self::NamedExplicitSupressesImplicit { backtrace, .. } => backtrace,
+            Self::UnnamedExplicitSupressesImplicit(_, backtrace, _) => backtrace,
+            _ => panic!("ERROR IN TEST IMPLEMENTATION"),
+        }
+    }
+}
+
+type MyBacktrace = Backtrace;
+
+#[test]
+fn unit() {
+    assert!(TestErr::Unit.backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_no_backtrace() {
+    let err = TestErr::NamedImplicitNoBacktrace { field: 0 };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_name() {
+    let err = TestErr::NamedImplicitBacktraceByFieldName {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_type() {
+    let err = TestErr::NamedImplicitBacktraceByFieldType {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_name() {
+    let err = TestErr::NamedExplicitNoBacktraceByFieldName {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_type() {
+    let err = TestErr::NamedExplicitNoBacktraceByFieldType {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_explicit_backtrace() {
+    let err = TestErr::NamedExplicitBacktrace {
+        explicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_redundant() {
+    let err = TestErr::NamedExplicitNoBacktraceRedundant {
+        not_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_name_redundant() {
+    let err = TestErr::NamedExplicitBacktraceByFieldNameRedundant {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_type_redundant() {
+    let err = TestErr::NamedExplicitBacktraceByFieldTypeRedundant {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_supresses_implicit() {
+    let err = TestErr::NamedExplicitSupressesImplicit {
+        not_backtrace: Backtrace::force_capture(),
+        backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+    assert_bt!(!=, err, .get_unused_backtrace);
+}
+
+#[test]
+fn unnamed_implicit_no_backtrace() {
+    let err = TestErr::UnnamedImplicitNoBacktrace(0, 0);
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn unnamed_implicit_backtrace() {
+    let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0, 0);
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace() {
+    let err = TestErr::UnnamedExplicitNoBacktrace(Backtrace::force_capture(), 0);
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace() {
+    let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0, 0);
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace_redundant() {
+    let err =
+        TestErr::UnnamedExplicitNoBacktraceRedundant(Backtrace::force_capture(), 0);
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace_redundant() {
+    let err =
+        TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0, 0);
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn unnamed_explicit_supresses_implicit() {
+    let err = TestErr::UnnamedExplicitSupressesImplicit(
+        Backtrace::force_capture(),
+        (|| Backtrace::force_capture())(), // ensure backtraces are different
+        0,
+    );
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+    assert_bt!(!=, err, .get_unused_backtrace);
+}
diff --git a/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs b/tests/error/nightly/derives_for_generic_enums_with_backtrace.rs
new file mode 100644 (file)
index 0000000..8574751
--- /dev/null
@@ -0,0 +1,272 @@
+use super::*;
+
+derive_display!(TestErr, T);
+#[derive(Debug, Error)]
+enum TestErr<T> {
+    Unit,
+    NamedImplicitNoBacktrace {
+        field: T,
+    },
+    NamedImplicitBacktraceByFieldName {
+        backtrace: MyBacktrace,
+        field: T,
+    },
+    NamedImplicitBacktraceByFieldType {
+        implicit_backtrace: Backtrace,
+        field: T,
+    },
+    NamedExplicitNoBacktraceByFieldName {
+        #[error(not(backtrace))]
+        backtrace: MyBacktrace,
+        field: T,
+    },
+    NamedExplicitNoBacktraceByFieldType {
+        #[error(not(backtrace))]
+        implicit_backtrace: Backtrace,
+        field: T,
+    },
+    NamedExplicitBacktrace {
+        #[error(backtrace)]
+        explicit_backtrace: MyBacktrace,
+        field: T,
+    },
+    NamedExplicitNoBacktraceRedundant {
+        #[error(not(backtrace))]
+        not_backtrace: MyBacktrace,
+        #[error(not(backtrace))]
+        field: T,
+    },
+    NamedExplicitBacktraceByFieldNameRedundant {
+        #[error(backtrace)]
+        backtrace: MyBacktrace,
+        field: T,
+    },
+    NamedExplicitBacktraceByFieldTypeRedundant {
+        #[error(backtrace)]
+        implicit_backtrace: Backtrace,
+        field: T,
+    },
+    NamedExplicitSupressesImplicit {
+        #[error(backtrace)]
+        not_backtrace: MyBacktrace,
+        backtrace: Backtrace,
+        field: T,
+    },
+    UnnamedImplicitNoBacktrace(T, T),
+    UnnamedImplicitBacktrace(Backtrace, T, T),
+    UnnamedExplicitNoBacktrace(#[error(not(backtrace))] Backtrace, T),
+    UnnamedExplicitBacktrace(#[error(backtrace)] MyBacktrace, T, T),
+    UnnamedExplicitNoBacktraceRedundant(
+        #[error(not(backtrace))] MyBacktrace,
+        #[error(not(backtrace))] T,
+    ),
+    UnnamedExplicitBacktraceRedundant(#[error(backtrace)] Backtrace, T, T),
+    UnnamedExplicitSupressesImplicit(#[error(backtrace)] MyBacktrace, Backtrace, T),
+}
+
+impl<T> TestErr<T> {
+    fn get_stored_backtrace(&self) -> &Backtrace {
+        match self {
+            Self::NamedImplicitBacktraceByFieldName { backtrace, .. } => backtrace,
+            Self::NamedImplicitBacktraceByFieldType {
+                implicit_backtrace, ..
+            } => implicit_backtrace,
+            Self::NamedExplicitBacktrace {
+                explicit_backtrace, ..
+            } => explicit_backtrace,
+            Self::NamedExplicitBacktraceByFieldNameRedundant { backtrace, .. } => {
+                backtrace
+            }
+            Self::NamedExplicitBacktraceByFieldTypeRedundant {
+                implicit_backtrace,
+                ..
+            } => implicit_backtrace,
+            Self::NamedExplicitSupressesImplicit { not_backtrace, .. } => not_backtrace,
+            Self::UnnamedImplicitBacktrace(backtrace, _, _) => backtrace,
+            Self::UnnamedExplicitBacktrace(backtrace, _, _) => backtrace,
+            Self::UnnamedExplicitBacktraceRedundant(backtrace, _, _) => backtrace,
+            Self::UnnamedExplicitSupressesImplicit(backtrace, _, _) => backtrace,
+            _ => panic!("ERROR IN TEST IMPLEMENTATION"),
+        }
+    }
+
+    fn get_unused_backtrace(&self) -> &Backtrace {
+        match self {
+            Self::NamedExplicitSupressesImplicit { backtrace, .. } => backtrace,
+            Self::UnnamedExplicitSupressesImplicit(_, backtrace, _) => backtrace,
+            _ => panic!("ERROR IN TEST IMPLEMENTATION"),
+        }
+    }
+}
+
+type MyBacktrace = Backtrace;
+
+#[test]
+fn unit() {
+    assert!(TestErr::<i32>::Unit.backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_no_backtrace() {
+    let err = TestErr::NamedImplicitNoBacktrace { field: 0 };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_name() {
+    let err = TestErr::NamedImplicitBacktraceByFieldName {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_type() {
+    let err = TestErr::NamedImplicitBacktraceByFieldType {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_name() {
+    let err = TestErr::NamedExplicitNoBacktraceByFieldName {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_type() {
+    let err = TestErr::NamedExplicitNoBacktraceByFieldType {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_explicit_backtrace() {
+    let err = TestErr::NamedExplicitBacktrace {
+        explicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_redundant() {
+    let err = TestErr::NamedExplicitNoBacktraceRedundant {
+        not_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_name_redundant() {
+    let err = TestErr::NamedExplicitBacktraceByFieldNameRedundant {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_type_redundant() {
+    let err = TestErr::NamedExplicitBacktraceByFieldTypeRedundant {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn named_explicit_supresses_implicit() {
+    let err = TestErr::NamedExplicitSupressesImplicit {
+        not_backtrace: Backtrace::force_capture(),
+        backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+    assert_bt!(!=, err, .get_unused_backtrace);
+}
+
+#[test]
+fn unnamed_implicit_no_backtrace() {
+    let err = TestErr::UnnamedImplicitNoBacktrace(0, 0);
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn unnamed_implicit_backtrace() {
+    let err = TestErr::UnnamedImplicitBacktrace(Backtrace::force_capture(), 0, 0);
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace() {
+    let err = TestErr::UnnamedExplicitNoBacktrace(Backtrace::force_capture(), 0);
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace() {
+    let err = TestErr::UnnamedExplicitBacktrace(Backtrace::force_capture(), 0, 0);
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace_redundant() {
+    let err =
+        TestErr::UnnamedExplicitNoBacktraceRedundant(Backtrace::force_capture(), 0);
+
+    assert!(err.backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace_redundant() {
+    let err =
+        TestErr::UnnamedExplicitBacktraceRedundant(Backtrace::force_capture(), 0, 0);
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+}
+
+#[test]
+fn unnamed_explicit_supresses_implicit() {
+    let err = TestErr::UnnamedExplicitSupressesImplicit(
+        Backtrace::force_capture(),
+        (|| Backtrace::force_capture())(), // ensure backtraces are different
+        0,
+    );
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, .get_stored_backtrace);
+    assert_bt!(!=, err, .get_unused_backtrace);
+}
diff --git a/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs b/tests/error/nightly/derives_for_generic_structs_with_backtrace.rs
new file mode 100644 (file)
index 0000000..91df87c
--- /dev/null
@@ -0,0 +1,275 @@
+use super::*;
+
+#[test]
+fn named_implicit_no_backtrace() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T> {
+        field: T,
+    }
+
+    assert!(TestErr::<i32>::default().backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_name() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        backtrace: MyBacktrace,
+        field: T,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err);
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_type() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        implicit_backtrace: Backtrace,
+        field: T,
+    }
+
+    let err = TestErr {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, implicit_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_name() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(not(backtrace))]
+        backtrace: MyBacktrace,
+        field: T,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    assert!(TestErr {
+        backtrace: Backtrace::force_capture(),
+        field: 0
+    }
+    .backtrace()
+    .is_none());
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_type() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(not(backtrace))]
+        implicit_backtrace: Backtrace,
+        field: T,
+    }
+
+    assert!(TestErr {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0
+    }
+    .backtrace()
+    .is_none());
+}
+
+#[test]
+fn named_explicit_backtrace() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(backtrace)]
+        explicit_backtrace: MyBacktrace,
+        field: T,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        explicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, explicit_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(not(backtrace))]
+        not_backtrace: MyBacktrace,
+        #[error(not(backtrace))]
+        field: T,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    assert!(TestErr {
+        not_backtrace: Backtrace::force_capture(),
+        field: 0
+    }
+    .backtrace()
+    .is_none());
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_name_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(backtrace)]
+        backtrace: MyBacktrace,
+        field: T,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err);
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_type_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(backtrace)]
+        implicit_backtrace: Backtrace,
+        field: T,
+    }
+
+    let err = TestErr {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, implicit_backtrace);
+}
+
+#[test]
+fn named_explicit_supresses_implicit() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T> {
+        #[error(backtrace)]
+        not_backtrace: MyBacktrace,
+        backtrace: Backtrace,
+        field: T,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        not_backtrace: Backtrace::force_capture(),
+        backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, not_backtrace);
+    assert_bt!(!=, err);
+}
+
+#[test]
+fn unnamed_implicit_no_backtrace() {
+    derive_display!(TestErr, T);
+    #[derive(Default, Debug, Error)]
+    struct TestErr<T>(T, T);
+
+    assert!(TestErr::<i32>::default().backtrace().is_none());
+}
+
+#[test]
+fn unnamed_implicit_backtrace() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T>(Backtrace, T, T);
+
+    let err = TestErr(Backtrace::force_capture(), 0, 0);
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T>(#[error(not(backtrace))] Backtrace, T);
+
+    assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T>(#[error(backtrace)] MyBacktrace, T, T);
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr(Backtrace::force_capture(), 0, 0);
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T>(
+        #[error(not(backtrace))] MyBacktrace,
+        #[error(not(backtrace))] T,
+    );
+
+    type MyBacktrace = Backtrace;
+
+    assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace_redundant() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T>(#[error(backtrace)] Backtrace, T, T);
+
+    let err = TestErr(Backtrace::force_capture(), 0, 0);
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+}
+
+#[test]
+fn unnamed_explicit_supresses_implicit() {
+    derive_display!(TestErr, T);
+    #[derive(Debug, Error)]
+    struct TestErr<T>(#[error(backtrace)] MyBacktrace, Backtrace, T);
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr(
+        Backtrace::force_capture(),
+        (|| Backtrace::force_capture())(), // ensure backtraces are different
+        0,
+    );
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+    assert_bt!(!=, err, 1);
+}
diff --git a/tests/error/nightly/derives_for_structs_with_backtrace.rs b/tests/error/nightly/derives_for_structs_with_backtrace.rs
new file mode 100644 (file)
index 0000000..18e268f
--- /dev/null
@@ -0,0 +1,280 @@
+use super::*;
+
+#[test]
+fn unit() {
+    assert!(SimpleErr.backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_no_backtrace() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr {
+        field: i32,
+    }
+
+    assert!(TestErr::default().backtrace().is_none());
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_name() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        backtrace: MyBacktrace,
+        field: i32,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err);
+}
+
+#[test]
+fn named_implicit_backtrace_by_field_type() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        implicit_backtrace: Backtrace,
+        field: i32,
+    }
+
+    let err = TestErr {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, implicit_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_name() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(not(backtrace))]
+        backtrace: MyBacktrace,
+        field: i32,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    assert!(TestErr {
+        backtrace: Backtrace::force_capture(),
+        field: 0
+    }
+    .backtrace()
+    .is_none());
+}
+
+#[test]
+fn named_explicit_no_backtrace_by_field_type() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(not(backtrace))]
+        implicit_backtrace: Backtrace,
+        field: i32,
+    }
+
+    assert!(TestErr {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0
+    }
+    .backtrace()
+    .is_none());
+}
+
+#[test]
+fn named_explicit_backtrace() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(backtrace)]
+        explicit_backtrace: MyBacktrace,
+        field: i32,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        explicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, explicit_backtrace);
+}
+
+#[test]
+fn named_explicit_no_backtrace_redundant() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(not(backtrace))]
+        not_backtrace: MyBacktrace,
+        #[error(not(backtrace))]
+        field: i32,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    assert!(TestErr {
+        not_backtrace: Backtrace::force_capture(),
+        field: 0
+    }
+    .backtrace()
+    .is_none());
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_name_redundant() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(backtrace)]
+        backtrace: MyBacktrace,
+        field: i32,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err);
+}
+
+#[test]
+fn named_explicit_backtrace_by_field_type_redundant() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(backtrace)]
+        implicit_backtrace: Backtrace,
+        field: i32,
+    }
+
+    let err = TestErr {
+        implicit_backtrace: Backtrace::force_capture(),
+        field: 0,
+    };
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, implicit_backtrace);
+}
+
+#[test]
+fn named_explicit_supresses_implicit() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr {
+        #[error(backtrace)]
+        not_backtrace: MyBacktrace,
+        backtrace: Backtrace,
+        field: i32,
+    }
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr {
+        not_backtrace: Backtrace::force_capture(),
+        backtrace: (|| Backtrace::force_capture())(), // ensure backtraces are different
+        field: 0,
+    };
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, not_backtrace);
+    assert_bt!(!=, err);
+}
+
+#[test]
+fn unnamed_implicit_no_backtrace() {
+    derive_display!(TestErr);
+    #[derive(Default, Debug, Error)]
+    struct TestErr(i32, i32);
+
+    assert!(TestErr::default().backtrace().is_none());
+}
+
+#[test]
+fn unnamed_implicit_backtrace() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr(Backtrace, i32, i32);
+
+    let err = TestErr(Backtrace::force_capture(), 0, 0);
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr(#[error(not(backtrace))] Backtrace, i32);
+
+    assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr(#[error(backtrace)] MyBacktrace, i32, i32);
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr(Backtrace::force_capture(), 0, 0);
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+}
+
+#[test]
+fn unnamed_explicit_no_backtrace_redundant() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr(
+        #[error(not(backtrace))] MyBacktrace,
+        #[error(not(backtrace))] i32,
+    );
+
+    type MyBacktrace = Backtrace;
+
+    assert!(TestErr(Backtrace::force_capture(), 0).backtrace().is_none());
+}
+
+#[test]
+fn unnamed_explicit_backtrace_redundant() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr(#[error(backtrace)] Backtrace, i32, i32);
+
+    let err = TestErr(Backtrace::force_capture(), 0, 0);
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+}
+
+#[test]
+fn unnamed_explicit_supresses_implicit() {
+    derive_display!(TestErr);
+    #[derive(Debug, Error)]
+    struct TestErr(#[error(backtrace)] MyBacktrace, Backtrace, i32);
+
+    type MyBacktrace = Backtrace;
+
+    let err = TestErr(
+        Backtrace::force_capture(),
+        (|| Backtrace::force_capture())(), // ensure backtraces are different
+        0,
+    );
+
+    assert!(err.backtrace().is_some());
+    assert_bt!(==, err, 0);
+    assert_bt!(!=, err, 1);
+}
diff --git a/tests/error/nightly/mod.rs b/tests/error/nightly/mod.rs
new file mode 100644 (file)
index 0000000..57de7ba
--- /dev/null
@@ -0,0 +1,85 @@
+use std::backtrace::Backtrace;
+
+use super::*;
+
+/// Asserts that backtrace returned by `Error::backtrace` method equals/not-equals
+/// backtrace stored in object itself.
+///
+/// Comparison is done by converting backtraces to strings
+/// and then comparing these strings.
+///
+/// ## Syntax
+///
+/// * Equals: `assert_bt!(==, ...)`
+/// * Not-equals: `assert_bt!(!=, ...)`
+///
+/// ### Backtrace Access
+///
+/// Shortcut for named-structs with `backtrace` field.
+/// Access backtrace as `error.backtrace`.
+///
+/// ```
+/// assert_bt!(==, error);
+/// ```
+///
+/// Full form for named- and tuple-structs.
+/// Access backtrace as `error.some_other_field` and `error.1` respectively.
+///
+/// ```
+/// assert_bt!(!=, error, some_other_field);
+/// assert_bt!(==, error, 1);
+/// ```
+///
+/// Access as a method call.
+/// Useful for enums (i.e., you can define a method that will match on enum variants
+/// and return backtrace for each variant).
+/// Access backtrace as `error.get_stored_backtrace_method()`.
+///
+/// ```
+/// assert_bt!(!=, error, .get_stored_backtrace_method);
+/// ```
+macro_rules! assert_bt {
+    (@impl $macro:ident, $error:expr, $backtrace:expr) => {
+        $macro!($error.backtrace().unwrap().to_string(), $backtrace.to_string());
+    };
+    (@expand $macro:ident, $error:expr, .$backtrace:ident) => {
+        assert_bt!(@impl $macro, $error, $error.$backtrace())
+    };
+    (@expand $macro:ident, $error:expr, $backtrace:tt) => {
+        assert_bt!(@impl $macro, $error, $error.$backtrace)
+    };
+    (@expand $macro:ident, $error:expr) => {
+        assert_bt!(@expand $macro, $error, backtrace)
+    };
+    (==, $($args:tt)*) => {
+        assert_bt!(@expand assert_eq, $($args)*)
+    };
+    (!=, $($args:tt)*) => {
+        assert_bt!(@expand assert_ne, $($args)*)
+    };
+}
+
+mod derives_for_enums_with_backtrace;
+mod derives_for_generic_enums_with_backtrace;
+mod derives_for_generic_structs_with_backtrace;
+mod derives_for_structs_with_backtrace;
+
+derive_display!(BacktraceErr);
+#[derive(Debug)]
+struct BacktraceErr {
+    backtrace: Backtrace,
+}
+
+impl Default for BacktraceErr {
+    fn default() -> Self {
+        Self {
+            backtrace: Backtrace::force_capture(),
+        }
+    }
+}
+
+impl Error for BacktraceErr {
+    fn backtrace(&self) -> Option<&Backtrace> {
+        Some(&self.backtrace)
+    }
+}
diff --git a/tests/error_tests.rs b/tests/error_tests.rs
new file mode 100644 (file)
index 0000000..c2798bd
--- /dev/null
@@ -0,0 +1,6 @@
+#![cfg_attr(feature = "nightly", feature(backtrace))]
+
+#[macro_use]
+extern crate derive_more;
+
+mod error;
diff --git a/tests/from.rs b/tests/from.rs
new file mode 100644 (file)
index 0000000..63bb102
--- /dev/null
@@ -0,0 +1,187 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+use std::borrow::Cow;
+
+#[derive(From)]
+struct EmptyTuple();
+
+#[derive(From)]
+struct EmptyStruct {}
+
+#[derive(From)]
+struct EmptyUnit;
+
+#[derive(From)]
+struct MyInt(i32);
+
+#[derive(From)]
+struct MyInts(i32, i32);
+
+#[derive(From)]
+struct Point1D {
+    x: i32,
+}
+
+#[derive(From)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(From)]
+enum MixedInts {
+    SmallInt(i32),
+    NamedBigInt {
+        int: i64,
+    },
+    TwoSmallInts(i32, i32),
+    NamedBigInts {
+        x: i64,
+        y: i64,
+    },
+    #[from(ignore)]
+    Unsigned(u32),
+    NamedUnsigned {
+        x: u32,
+    },
+}
+
+#[derive(PartialEq, Eq, Debug)]
+#[derive(From)]
+#[from(forward)]
+struct MyIntForward(u64);
+
+#[test]
+fn forward_struct() {
+    assert_eq!(MyIntForward(42), 42u32.into());
+    assert_eq!(MyIntForward(42), 42u16.into());
+    assert_eq!(MyIntForward(42), 42u64.into());
+}
+
+#[derive(PartialEq, Eq, Debug)]
+#[derive(From)]
+enum MixedIntsForward {
+    #[from(forward)]
+    SmallInt(i32),
+    NamedBigInt {
+        int: i64,
+    },
+}
+
+#[test]
+fn forward_enum() {
+    assert_eq!(MixedIntsForward::SmallInt(42), 42i32.into());
+    assert_eq!(MixedIntsForward::SmallInt(42), 42i16.into());
+}
+
+#[derive(From, PartialEq)]
+enum AutoIgnore {
+    SmallInt(i32),
+    Uninteresting,
+    Uninteresting2,
+}
+
+#[test]
+fn auto_ignore_variants() {
+    assert!(AutoIgnore::SmallInt(42) == 42i32.into());
+}
+
+#[derive(From, PartialEq)]
+enum AutoIgnoreWithDefaultTrue {
+    #[from(ignore)]
+    SmallInt(i32),
+    Uninteresting,
+    Uninteresting2,
+}
+
+#[derive(From, PartialEq)]
+enum AutoIgnoreWithForwardFields2 {
+    #[from(forward)]
+    SmallInt(i32),
+    SmallIntIgnore(i32),
+}
+
+#[test]
+fn auto_ignore_with_forward_field2() {
+    assert!(AutoIgnoreWithForwardFields2::SmallInt(42) == 42i32.into());
+    assert!(AutoIgnoreWithForwardFields2::SmallInt(42) == 42i16.into());
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[derive(From)]
+#[from(types(u8, u16, u32))]
+struct MyIntExplicit(u64);
+
+#[test]
+fn explicit_types_struct() {
+    assert_eq!(MyIntExplicit(42), 42u8.into());
+    assert_eq!(MyIntExplicit(42), 42u16.into());
+    assert_eq!(MyIntExplicit(42), 42u32.into());
+    assert_eq!(MyIntExplicit(42), 42u64.into());
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[derive(From)]
+#[from(types(i8, i16))]
+struct MyIntsExplicit(i32, i32);
+
+#[test]
+fn explicit_types_struct_tupled() {
+    assert_eq!(MyIntsExplicit(42, 42), (42i32, 42i32).into());
+    assert_eq!(MyIntsExplicit(42, 42), (42i8, 42i8).into());
+    assert_eq!(MyIntsExplicit(42, 42), (42i16, 42i16).into());
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[derive(From)]
+enum MixedIntsExplicit {
+    #[from(types(i8))]
+    SmallInt(i32),
+    #[from(types(i16, i64))]
+    AnotherInt(i128),
+    NamedBigInt {
+        int: i64,
+    },
+}
+
+#[test]
+fn explicit_types_enum() {
+    assert_eq!(MixedIntsExplicit::SmallInt(42), 42i32.into());
+    assert_eq!(MixedIntsExplicit::SmallInt(42), 42i8.into());
+
+    assert_eq!(MixedIntsExplicit::AnotherInt(42), 42i128.into());
+    assert_eq!(MixedIntsExplicit::AnotherInt(42), 42i64.into());
+    assert_eq!(MixedIntsExplicit::AnotherInt(42), 42i16.into());
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[derive(From)]
+#[from(types(i8, i16))]
+struct Point2DExplicit {
+    x: i32,
+    y: i32,
+}
+
+#[test]
+fn explicit_types_point_2d() {
+    let expected = Point2DExplicit { x: 42, y: 42 };
+    assert_eq!(expected, (42i32, 42i32).into());
+    assert_eq!(expected, (42i8, 42i8).into());
+    assert_eq!(expected, (42i16, 42i16).into());
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[derive(From)]
+#[from(types("Cow<'_, str>", "&str"))]
+struct Name(String);
+
+#[test]
+fn explicit_complex_types_name() {
+    let name = "Eärendil";
+    let expected = Name(name.to_owned());
+    assert_eq!(expected, name.to_owned().into());
+    assert_eq!(expected, name.into());
+    assert_eq!(expected, Cow::Borrowed(name).into());
+}
diff --git a/tests/from_str.rs b/tests/from_str.rs
new file mode 100644 (file)
index 0000000..4bb0eca
--- /dev/null
@@ -0,0 +1,11 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(FromStr)]
+struct MyInt(i32);
+
+#[derive(FromStr)]
+struct Point1D {
+    x: i32,
+}
diff --git a/tests/generics.rs b/tests/generics.rs
new file mode 100644 (file)
index 0000000..17af5f3
--- /dev/null
@@ -0,0 +1,123 @@
+#![allow(dead_code, non_camel_case_types)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(
+    From,
+    FromStr,
+    Display,
+    Index,
+    Not,
+    Add,
+    Mul,
+    Sum,
+    IndexMut,
+    AddAssign,
+    MulAssign,
+    Deref,
+    DerefMut,
+    IntoIterator,
+    Constructor
+)]
+#[deref(forward)]
+#[deref_mut(forward)]
+#[into_iterator(owned, ref, ref_mut)]
+struct Wrapped<T: Clone>(T);
+
+#[derive(Deref, DerefMut)]
+struct Wrapped2<T: Clone>(T);
+
+#[derive(From, Not, Add, Mul, AddAssign, MulAssign, Constructor, Sum)]
+struct WrappedDouble<T: Clone, U: Clone>(T, U);
+
+#[derive(From)]
+#[from(forward)]
+struct WrappedDouble2<T: Clone, U: Clone>(T, U);
+
+#[cfg(feature = "nightly")]
+#[derive(
+    From,
+    FromStr,
+    Display,
+    Index,
+    Not,
+    Add,
+    Mul,
+    Sum,
+    IndexMut,
+    AddAssign,
+    MulAssign,
+    Deref,
+    DerefMut,
+    IntoIterator,
+    Constructor
+)]
+struct WrappedWithConst<T, const C: u32>(T);
+
+#[derive(
+    From,
+    FromStr,
+    Display,
+    Index,
+    Not,
+    Add,
+    Mul,
+    IndexMut,
+    AddAssign,
+    MulAssign,
+    Deref,
+    DerefMut,
+    IntoIterator,
+    Constructor,
+    Sum
+)]
+#[deref(forward)]
+#[deref_mut(forward)]
+#[into_iterator(owned, ref, ref_mut)]
+struct Struct<T: Clone> {
+    t: T,
+}
+
+#[derive(Deref, DerefMut)]
+struct Struct2<T: Clone> {
+    t: T,
+}
+
+#[derive(From, Not, Add, Mul, AddAssign, MulAssign, Constructor, Sum)]
+struct DoubleStruct<T: Clone, U: Clone> {
+    t: T,
+    u: U,
+}
+
+#[derive(From)]
+#[from(forward)]
+struct DoubleStruct2<T: Clone, U: Clone> {
+    t: T,
+    u: U,
+}
+
+#[derive(From, Not, Add)]
+enum TupleEnum<T: Clone, U: Clone> {
+    Tuple(T),
+    DoubleTuple(T, U),
+}
+
+#[derive(From)]
+#[from(forward)]
+enum TupleEnum2<T: Clone, U: Clone, X: Clone> {
+    DoubleTuple(T, U),
+    TripleTuple(T, U, X),
+}
+
+#[derive(From, Not, Add)]
+enum StructEnum<T: Clone, U: Clone> {
+    Struct { t: T },
+    DoubleStruct { t: T, u: U },
+}
+
+#[derive(From)]
+#[from(forward)]
+enum StructEnum2<T: Clone, U: Clone, X: Clone> {
+    DoubleStruct { t: T, u: U },
+    TripleStruct { t: T, u: U, x: X },
+}
diff --git a/tests/index.rs b/tests/index.rs
new file mode 100644 (file)
index 0000000..952223c
--- /dev/null
@@ -0,0 +1,13 @@
+#![allow(dead_code, unused_imports)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Index)]
+struct MyVec(Vec<i32>);
+
+#[derive(Index)]
+struct Numbers {
+    #[index]
+    numbers: Vec<i32>,
+    useless: bool,
+}
diff --git a/tests/index_mut.rs b/tests/index_mut.rs
new file mode 100644 (file)
index 0000000..a053fd3
--- /dev/null
@@ -0,0 +1,36 @@
+#![allow(dead_code, unused_imports)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(IndexMut)]
+struct MyVec(Vec<i32>);
+//Index implementation is required for IndexMut
+impl<__IdxT> ::core::ops::Index<__IdxT> for MyVec
+where
+    Vec<i32>: ::core::ops::Index<__IdxT>,
+{
+    type Output = <Vec<i32> as ::core::ops::Index<__IdxT>>::Output;
+    #[inline]
+    fn index(&self, idx: __IdxT) -> &Self::Output {
+        <Vec<i32> as ::core::ops::Index<__IdxT>>::index(&self.0, idx)
+    }
+}
+
+#[derive(IndexMut)]
+struct Numbers {
+    #[index_mut]
+    numbers: Vec<i32>,
+    useless: bool,
+}
+
+//Index implementation is required for IndexMut
+impl<__IdxT> ::core::ops::Index<__IdxT> for Numbers
+where
+    Vec<i32>: ::core::ops::Index<__IdxT>,
+{
+    type Output = <Vec<i32> as ::core::ops::Index<__IdxT>>::Output;
+    #[inline]
+    fn index(&self, idx: __IdxT) -> &Self::Output {
+        <Vec<i32> as ::core::ops::Index<__IdxT>>::index(&self.numbers, idx)
+    }
+}
diff --git a/tests/into.rs b/tests/into.rs
new file mode 100644 (file)
index 0000000..075abd1
--- /dev/null
@@ -0,0 +1,141 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+use std::borrow::Cow;
+
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct EmptyTuple();
+
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct EmptyStruct {}
+
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct EmptyUnit;
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Into)]
+#[into(owned(types(i64, i128)), ref, ref_mut)]
+struct MyInt(i32);
+
+#[test]
+fn explicit_types_struct_owned_only() {
+    assert_eq!(i32::from(MyInt(42)), 42i32);
+    assert_eq!(<&i32>::from(&MyInt(42)), &42i32);
+    assert_eq!(<&mut i32>::from(&mut MyInt(42)), &mut 42i32);
+    assert_eq!(i64::from(MyInt(42)), 42i64);
+    assert_eq!(i128::from(MyInt(42)), 42i128);
+}
+
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct MyInts(i32, i32);
+
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct Point1D {
+    x: i32,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(Into)]
+#[into(owned, ref, ref_mut)]
+struct Point2DWithIgnored {
+    x: i32,
+    y: i32,
+    #[into(ignore)]
+    useless: bool,
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Into)]
+#[into(owned(types(i64, i128)), ref, ref_mut, types(i32))]
+struct MyIntExplicit(MyInt);
+
+#[test]
+fn explicit_types_struct_all() {
+    let mut input = MyIntExplicit(MyInt(42));
+    assert_eq!(MyInt::from(input), MyInt(42));
+    assert_eq!(<&MyInt>::from(&input), &MyInt(42));
+    assert_eq!(<&mut MyInt>::from(&mut input), &mut MyInt(42));
+    assert_eq!(i32::from(input), 42i32);
+    assert_eq!(<&i32>::from(&input), &42i32);
+    assert_eq!(<&mut i32>::from(&mut input), &mut 42i32);
+    assert_eq!(i64::from(input), 42i64);
+    assert_eq!(i128::from(input), 42i128);
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Into)]
+#[into(owned(types(i32, i64, i128)), ref(types(i32)), ref_mut(types(i32)))]
+struct MyIntsExplicit(i32, MyInt, MyIntExplicit);
+
+#[test]
+fn explicit_types_struct_tupled() {
+    let mut input = MyIntsExplicit(42i32, MyInt(42), MyIntExplicit(MyInt(42)));
+    assert_eq!(
+        <(i32, MyInt, MyIntExplicit)>::from(input),
+        (42i32, MyInt(42), MyIntExplicit(MyInt(42))),
+    );
+    assert_eq!(
+        <(&i32, &MyInt, &MyIntExplicit)>::from(&input),
+        (&42i32, &MyInt(42), &MyIntExplicit(MyInt(42))),
+    );
+    assert_eq!(
+        <(&mut i32, &mut MyInt, &mut MyIntExplicit)>::from(&mut input),
+        (&mut 42i32, &mut MyInt(42), &mut MyIntExplicit(MyInt(42))),
+    );
+    assert_eq!(<(i32, i32, i32)>::from(input), (42i32, 42i32, 42i32));
+    assert_eq!(<(&i32, &i32, &i32)>::from(&input), (&42i32, &42i32, &42i32));
+    assert_eq!(
+        <(&mut i32, &mut i32, &mut i32)>::from(&mut input),
+        (&mut 42i32, &mut 42i32, &mut 42i32),
+    );
+    assert_eq!(<(i64, i64, i64)>::from(input), (42i64, 42i64, 42i64));
+    assert_eq!(<(i128, i128, i128)>::from(input), (42i128, 42i128, 42i128));
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Into)]
+#[into(owned, ref, ref_mut, types(i32))]
+struct Point2DExplicit {
+    x: MyInt,
+    y: MyInt,
+}
+
+#[test]
+fn explicit_types_point_2d() {
+    let mut input = Point2DExplicit {
+        x: MyInt(42),
+        y: MyInt(42),
+    };
+    assert_eq!(<(i32, i32)>::from(input), (42i32, 42i32));
+    assert_eq!(<(&i32, &i32)>::from(&input), (&42i32, &42i32));
+    assert_eq!(
+        <(&mut i32, &mut i32)>::from(&mut input),
+        (&mut 42i32, &mut 42i32)
+    );
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Into)]
+#[into(owned(types("Cow<'_, str>")))]
+struct Name(String);
+
+#[test]
+fn explicit_complex_types_name() {
+    let name = "Ñolofinwë";
+    let input = Name(name.to_owned());
+    assert_eq!(String::from(input.clone()), name.to_owned());
+    assert_eq!(Cow::from(input.clone()), Cow::Borrowed(name));
+}
diff --git a/tests/into_iterator.rs b/tests/into_iterator.rs
new file mode 100644 (file)
index 0000000..8c0e48f
--- /dev/null
@@ -0,0 +1,39 @@
+#![allow(dead_code, unused_imports)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(IntoIterator)]
+#[into_iterator(owned, ref, ref_mut)]
+struct MyVec(Vec<i32>);
+
+#[derive(IntoIterator)]
+#[into_iterator(owned, ref, ref_mut)]
+struct Numbers {
+    numbers: Vec<i32>,
+}
+
+#[derive(IntoIterator)]
+struct Numbers2 {
+    #[into_iterator(owned, ref, ref_mut)]
+    numbers: Vec<i32>,
+    useless: bool,
+    useless2: bool,
+}
+
+#[derive(IntoIterator)]
+struct Numbers3 {
+    #[into_iterator(ref, ref_mut)]
+    numbers: Vec<i32>,
+    useless: bool,
+    useless2: bool,
+}
+
+// Test that owned is not enabled when ref/ref_mut are enabled without owned
+impl ::core::iter::IntoIterator for Numbers3 {
+    type Item = <Vec<i32> as ::core::iter::IntoIterator>::Item;
+    type IntoIter = <Vec<i32> as ::core::iter::IntoIterator>::IntoIter;
+    #[inline]
+    fn into_iter(self) -> Self::IntoIter {
+        <Vec<i32> as ::core::iter::IntoIterator>::into_iter(self.numbers)
+    }
+}
diff --git a/tests/is_variant.rs b/tests/is_variant.rs
new file mode 100644 (file)
index 0000000..10876a4
--- /dev/null
@@ -0,0 +1,57 @@
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate derive_more;
+
+#[derive(IsVariant)]
+enum Either<TLeft, TRight> {
+    Left(TLeft),
+    Right(TRight),
+}
+
+#[derive(IsVariant)]
+enum Maybe<T> {
+    Nothing,
+    Just(T),
+}
+
+#[derive(IsVariant)]
+enum Color {
+    RGB(u8, u8, u8),
+    CMYK { c: u8, m: u8, y: u8, k: u8 },
+}
+
+#[derive(IsVariant)]
+enum Nonsense<'a, T> {
+    Ref(&'a T),
+    NoRef,
+    #[is_variant(ignore)]
+    NoRefIgnored,
+}
+
+#[derive(IsVariant)]
+enum WithConstraints<T>
+where
+    T: Copy,
+{
+    One(T),
+    Two,
+}
+#[derive(IsVariant)]
+enum KitchenSink<'a, 'b, T1: Copy, T2: Clone>
+where
+    T2: Into<T1> + 'b,
+{
+    Left(&'a T1),
+    Right(&'b T2),
+    OwnBoth { left: T1, right: T2 },
+    Empty,
+    NeverMind(),
+    NothingToSeeHere {},
+}
+
+#[test]
+pub fn test_is_variant() {
+    assert!(Maybe::<()>::Nothing.is_nothing());
+    assert!(!Maybe::<()>::Nothing.is_just());
+}
diff --git a/tests/lib.rs b/tests/lib.rs
new file mode 100644 (file)
index 0000000..27fd53d
--- /dev/null
@@ -0,0 +1,275 @@
+#[macro_use]
+extern crate derive_more;
+
+#[derive(From)]
+#[derive(Into)]
+#[derive(Constructor)]
+#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Add)]
+#[derive(Mul)]
+#[derive(Neg)]
+#[derive(AddAssign)]
+#[derive(MulAssign)]
+#[derive(FromStr)]
+#[derive(Display)]
+#[derive(Octal)]
+#[derive(Binary)]
+#[derive(Deref, DerefMut)]
+#[into(owned, ref, ref_mut)]
+struct MyInt(i32);
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Add)]
+#[derive(Sum)]
+#[derive(Mul)]
+#[derive(MulAssign)]
+#[derive(Product)]
+#[mul(forward)]
+#[mul_assign(forward)]
+struct MyInt2(i32);
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Index, IndexMut)]
+#[derive(Deref, DerefMut)]
+#[derive(IntoIterator)]
+#[deref(forward)]
+#[deref_mut(forward)]
+#[into_iterator(owned, ref, ref_mut)]
+struct MyVec(Vec<i32>);
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Deref, DerefMut)]
+#[deref(forward)]
+#[deref_mut(forward)]
+struct MyBoxedInt(Box<i32>);
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Not)]
+#[derive(From)]
+struct MyBool(bool);
+
+#[derive(From)]
+#[derive(Into)]
+#[derive(Constructor)]
+#[derive(Add)]
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Mul)]
+#[derive(AddAssign)]
+struct MyUInt(u64, u64);
+
+#[derive(From)]
+#[derive(Into)]
+#[derive(Constructor)]
+#[derive(FromStr)]
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Display)]
+struct SimpleStruct {
+    int1: u64,
+}
+
+#[derive(From)]
+#[derive(Constructor)]
+#[derive(Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shr, Shl)]
+#[derive(Eq, PartialEq, Debug, Clone, Copy)]
+#[derive(Into)]
+#[derive(AddAssign)]
+#[into(owned, ref, ref_mut)]
+struct NormalStruct {
+    int1: u64,
+    int2: u64,
+}
+
+#[derive(From)]
+#[derive(Eq, PartialEq, Debug)]
+struct NestedInt(MyInt);
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(From)]
+#[derive(Add, Sub)]
+enum SimpleMyIntEnum {
+    Int(i32),
+    #[from(ignore)]
+    _UnsignedOne(u32),
+    _UnsignedTwo(u32),
+}
+#[derive(Eq, PartialEq, Debug)]
+#[derive(From)]
+#[derive(Neg)]
+enum SimpleSignedIntEnum {
+    Int(i32),
+    Int2(i16),
+}
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(From)]
+#[derive(Add, Sub)]
+#[derive(Neg)]
+enum SimpleEnum {
+    Int(i32),
+    #[from(ignore)]
+    _Ints(i32, i32),
+    LabeledInts {
+        a: i32,
+        b: i32,
+    },
+    _SomeUnit,
+}
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(From)]
+#[derive(Add, Sub)]
+enum MyIntEnum {
+    SmallInt(i32),
+    BigInt(i64),
+    TwoInts(i32, i32),
+    Point2D {
+        x: i64,
+        y: i64,
+    },
+    #[from(ignore)]
+    _UnsignedOne(u32),
+    _UnsignedTwo(u32),
+    #[from(ignore)]
+    _Uints1(u64, u64),
+    _Uints2 {
+        x: u64,
+        y: u64,
+    },
+    Nothing,
+}
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Add, Mul)]
+struct DoubleUInt(u32, u32);
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(Add, Mul)]
+struct DoubleUIntStruct {
+    x: u32,
+    y: u32,
+}
+
+#[derive(Eq, PartialEq, Debug)]
+#[derive(From, Into, Constructor)]
+struct Unit;
+
+// Tests that we can forward to a path
+// containing `$crate`
+macro_rules! use_dollar_crate {
+    () => {
+        struct Foo;
+        #[derive(From)]
+        enum Bar {
+            First(#[from(forward)] $crate::Foo),
+        }
+    };
+}
+
+use_dollar_crate!();
+
+#[test]
+fn main() {
+    let mut myint: MyInt = 5.into();
+    let _: SimpleMyIntEnum = 5i32.into();
+    let _: MyIntEnum = 5i32.into();
+    let _: MyIntEnum = 6i64.into();
+    let _: MyIntEnum = (5i32, 8i32).into();
+    let _: MyIntEnum = (5i64, 8i64).into();
+    let _: MyIntEnum = ().into();
+
+    let int_ref: &i32 = (&myint).into();
+    assert_eq!(int_ref, &5);
+
+    let int_ref_mut: &mut i32 = (&mut myint).into();
+    assert_eq!(int_ref_mut, &mut 5);
+
+    let mut myint: MyInt = 5.into();
+    let _: Unit = ().into();
+    assert_eq!((), Unit.into());
+    assert_eq!(Unit, Unit::new());
+    assert_eq!(MyInt(5), 5.into());
+    assert_eq!(Ok(MyInt(5)), "5".parse());
+    assert_eq!(5, MyInt(5).into());
+    assert_eq!(MyInt(5), MyInt::new(5));
+    assert_eq!(-MyInt(5), (-5).into());
+    assert_eq!("30", format!("{}", MyInt(30)));
+    assert_eq!("36", format!("{:o}", MyInt(30)));
+    assert_eq!("100", format!("{:b}", MyInt(4)));
+    assert_eq!(!MyBool(true), false.into());
+    assert_eq!(MyIntEnum::SmallInt(5), 5.into());
+
+    assert_eq!(SimpleStruct { int1: 5 }, 5.into());
+    assert_eq!(5u64, SimpleStruct { int1: 5 }.into());
+    assert_eq!(Ok(SimpleStruct { int1: 5 }), "5".parse());
+    assert_eq!("5", format!("{}", SimpleStruct { int1: 5 }));
+    assert_eq!(NormalStruct { int1: 5, int2: 6 }, (5, 6).into());
+    assert_eq!(SimpleStruct { int1: 5 }, SimpleStruct::new(5));
+    assert_eq!(NormalStruct { int1: 5, int2: 6 }, NormalStruct::new(5, 6));
+    assert_eq!((5, 6), NormalStruct::new(5, 6).into());
+    let mut norm_struct = NormalStruct::new(5, 6);
+    let uints_ref: (&u64, &u64) = (&norm_struct).into();
+    assert_eq!((&5, &6), uints_ref);
+    let uints_ref_mut: (&mut u64, &mut u64) = (&mut norm_struct).into();
+    assert_eq!((&mut 5, &mut 6), uints_ref_mut);
+
+    assert_eq!(MyInt(4) + MyInt(1), 5.into());
+    myint += MyInt(3);
+    assert_eq!(myint, 8.into());
+    myint *= 5;
+    assert_eq!(myint, 40.into());
+    assert_eq!(MyInt(4) + MyInt(1), 5.into());
+    assert_eq!(MyUInt(4, 5) + MyUInt(1, 2), MyUInt(5, 7));
+    assert_eq!(MyUInt(4, 5), MyUInt::new(4, 5));
+    assert_eq!((4, 5), MyUInt(4, 5).into());
+    let mut s1 = NormalStruct { int1: 1, int2: 2 };
+    let s2 = NormalStruct { int1: 2, int2: 3 };
+    let s3 = NormalStruct { int1: 3, int2: 5 };
+    assert_eq!(s1 + s2, s3);
+    assert_eq!(s3 - s2, s1);
+    s1 += s2;
+    assert_eq!(s1, s3);
+
+    assert_eq!((SimpleMyIntEnum::Int(6) + 5.into()).unwrap(), 11.into());
+    assert_eq!((SimpleMyIntEnum::Int(6) - 5.into()).unwrap(), 1.into());
+    assert_eq!((SimpleMyIntEnum::Int(6) - 5.into()).unwrap(), 1.into());
+    assert_eq!(-SimpleSignedIntEnum::Int(6), (-6i32).into());
+    assert_eq!(
+        (SimpleEnum::LabeledInts { a: 6, b: 5 }
+            + SimpleEnum::LabeledInts { a: 1, b: 4 })
+        .unwrap(),
+        SimpleEnum::LabeledInts { a: 7, b: 9 }
+    );
+
+    let _ = (MyIntEnum::SmallInt(5) + 6.into()).unwrap();
+    assert_eq!((-SimpleEnum::Int(5)).unwrap(), (-5).into());
+
+    assert_eq!(MyInt(50), MyInt(5) * 10);
+    assert_eq!(DoubleUInt(5, 6) * 10, DoubleUInt(50, 60));
+    // assert_eq!(DoubleUIntStruct{x:5, y:6} * 10, DoubleUIntStruct{x:50, y:60});
+
+    let mut myint = MyInt(5);
+    assert_eq!(5, *myint);
+    *myint = 7;
+    assert_eq!(MyInt(7), myint);
+
+    let mut my_vec = MyVec(vec![5, 8]);
+    assert_eq!(5, my_vec[0]);
+    assert_eq!(8, my_vec[1]);
+    my_vec[0] = 20;
+    assert_eq!(20, my_vec[0]);
+    assert_eq!((&my_vec).into_iter().next(), Some(&20));
+    assert_eq!((&mut my_vec).into_iter().next(), Some(&mut 20));
+    assert_eq!(my_vec.into_iter().next(), Some(20));
+
+    let int_vec = vec![MyInt2(2), MyInt2(3)];
+    assert_eq!(MyInt2(5), int_vec.clone().into_iter().sum());
+    assert_eq!(MyInt2(6), int_vec.clone().into_iter().product());
+    let mut myint2 = MyInt2(8);
+    myint2 *= MyInt2(4);
+    assert_eq!(MyInt2(32), myint2);
+
+    let mut boxed = MyBoxedInt(Box::new(5));
+    assert_eq!(5, *boxed);
+    *boxed = 7;
+    assert_eq!(MyBoxedInt(Box::new(7)), boxed)
+}
diff --git a/tests/mul.rs b/tests/mul.rs
new file mode 100644 (file)
index 0000000..1928060
--- /dev/null
@@ -0,0 +1,20 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Mul)]
+struct MyInt(i32);
+
+#[derive(Mul)]
+struct MyInts(i32, i32);
+
+#[derive(Mul)]
+struct Point1D {
+    x: i32,
+}
+
+#[derive(Mul)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
diff --git a/tests/mul_assign.rs b/tests/mul_assign.rs
new file mode 100644 (file)
index 0000000..f8aee3e
--- /dev/null
@@ -0,0 +1,32 @@
+#![allow(dead_code)]
+use std::marker::PhantomData;
+
+#[macro_use]
+extern crate derive_more;
+
+#[derive(MulAssign)]
+struct MyInt(i32);
+
+#[derive(MulAssign)]
+struct MyInts(i32, i32);
+
+#[derive(MulAssign)]
+#[mul_assign(forward)]
+struct MyIntForward(i32);
+
+#[derive(MulAssign)]
+struct Point1D {
+    x: i32,
+}
+
+#[derive(MulAssign)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(MulAssign)]
+struct MyInt2<T> {
+    x: i32,
+    ph: PhantomData<T>,
+}
diff --git a/tests/no_std.rs b/tests/no_std.rs
new file mode 100644 (file)
index 0000000..a6680c3
--- /dev/null
@@ -0,0 +1,74 @@
+#![no_std]
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate derive_more;
+
+#[derive(
+    AddAssign,
+    MulAssign,
+    Add,
+    Mul,
+    Not,
+    Index,
+    Display,
+    FromStr,
+    Into,
+    From,
+    IndexMut,
+    Sum,
+    Deref,
+    DerefMut,
+    Constructor
+)]
+#[into(owned, ref, ref_mut)]
+struct MyInts(u64);
+
+#[derive(Deref, DerefMut)]
+#[deref(forward)]
+#[deref_mut(forward)]
+struct MyBoxedInt<'a>(&'a mut u64);
+
+#[derive(
+    From,
+    FromStr,
+    Display,
+    Index,
+    Not,
+    Add,
+    Mul,
+    Sum,
+    IndexMut,
+    AddAssign,
+    Deref,
+    DerefMut,
+    IntoIterator,
+    Constructor
+)]
+#[deref(forward)]
+#[deref_mut(forward)]
+#[into_iterator(owned, ref, ref_mut)]
+struct Wrapped<T: Clone>(T);
+
+#[derive(Deref, DerefMut)]
+struct Wrapped2<T: Clone>(T);
+
+#[derive(From, Not, Add, Mul, AddAssign, Constructor, Sum)]
+struct WrappedDouble<T: Clone, U: Clone>(T, U);
+
+#[derive(Add, Not, TryInto)]
+#[try_into(owned, ref, ref_mut)]
+enum MixedInts {
+    SmallInt(i32),
+    BigInt(i64),
+    TwoSmallInts(i32, i32),
+    NamedSmallInts { x: i32, y: i32 },
+    UnsignedOne(u32),
+    UnsignedTwo(u32),
+}
+
+#[derive(Not, Add)]
+enum EnumWithUnit {
+    SmallInt(i32),
+    Unit,
+}
diff --git a/tests/not.rs b/tests/not.rs
new file mode 100644 (file)
index 0000000..7e3ad8d
--- /dev/null
@@ -0,0 +1,28 @@
+#![allow(dead_code)]
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Not)]
+struct MyInts(i32, i32);
+
+#[derive(Not)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+#[derive(Not)]
+enum MixedInts {
+    SmallInt(i32),
+    BigInt(i64),
+    TwoSmallInts(i32, i32),
+    NamedSmallInts { x: i32, y: i32 },
+    UnsignedOne(u32),
+    UnsignedTwo(u32),
+}
+
+#[derive(Not)]
+enum EnumWithUnit {
+    SmallInt(i32),
+    Unit,
+}
diff --git a/tests/sum.rs b/tests/sum.rs
new file mode 100644 (file)
index 0000000..a0b0967
--- /dev/null
@@ -0,0 +1,31 @@
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Sum)]
+struct MyInts(i32, i64);
+
+// Add implementation is needed for Sum
+impl ::core::ops::Add for MyInts {
+    type Output = MyInts;
+    #[inline]
+    fn add(self, rhs: MyInts) -> MyInts {
+        MyInts(self.0.add(rhs.0), self.1.add(rhs.1))
+    }
+}
+
+#[derive(Sum)]
+struct Point2D {
+    x: i32,
+    y: i32,
+}
+
+impl ::core::ops::Add for Point2D {
+    type Output = Point2D;
+    #[inline]
+    fn add(self, rhs: Point2D) -> Point2D {
+        Point2D {
+            x: self.x.add(rhs.x),
+            y: self.y.add(rhs.y),
+        }
+    }
+}
diff --git a/tests/try_into.rs b/tests/try_into.rs
new file mode 100644 (file)
index 0000000..837c8e9
--- /dev/null
@@ -0,0 +1,199 @@
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate derive_more;
+
+use std::convert::{TryFrom, TryInto};
+
+// Ensure that the TryFrom macro is hygenic and doesn't break when `Result` has
+// been redefined.
+type Result = ();
+
+#[derive(Clone, Copy, TryInto)]
+#[try_into(owned, ref, ref_mut)]
+enum MixedInts {
+    SmallInt(i32),
+    NamedBigInt {
+        int: i64,
+    },
+    UnsignedWithIgnoredField(#[try_into(ignore)] bool, i64),
+    NamedUnsignedWithIgnnoredField {
+        #[try_into(ignore)]
+        useless: bool,
+        x: i64,
+    },
+    TwoSmallInts(i32, i32),
+    NamedBigInts {
+        x: i64,
+        y: i64,
+    },
+    Unsigned(u32),
+    NamedUnsigned {
+        x: u32,
+    },
+    Unit,
+    #[try_into(ignore)]
+    Unit2,
+}
+
+#[test]
+fn test_try_into() {
+    let mut i = MixedInts::SmallInt(42);
+    assert_eq!(Ok(42i32), i.try_into());
+    assert_eq!(Ok(&42i32), (&i).try_into());
+    assert_eq!(Ok(&mut 42i32), (&mut i).try_into());
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(
+        <(i32, i32)>::try_from(i),
+        Err("Only TwoSmallInts can be converted to (i32, i32)")
+    );
+    assert_eq!(
+        <(i64, i64)>::try_from(i),
+        Err("Only NamedBigInts can be converted to (i64, i64)")
+    );
+    assert_eq!(
+        u32::try_from(i),
+        Err("Only Unsigned, NamedUnsigned can be converted to u32")
+    );
+    assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
+
+    let mut i = MixedInts::NamedBigInt { int: 42 };
+    assert_eq!(
+        i32::try_from(i),
+        Err("Only SmallInt can be converted to i32")
+    );
+    assert_eq!(Ok(42i64), i.try_into());
+    assert_eq!(Ok(&42i64), (&i).try_into());
+    assert_eq!(Ok(&mut 42i64), (&mut i).try_into());
+    assert_eq!(
+        <(i32, i32)>::try_from(i),
+        Err("Only TwoSmallInts can be converted to (i32, i32)")
+    );
+    assert_eq!(
+        <(i64, i64)>::try_from(i),
+        Err("Only NamedBigInts can be converted to (i64, i64)")
+    );
+    assert_eq!(
+        u32::try_from(i),
+        Err("Only Unsigned, NamedUnsigned can be converted to u32")
+    );
+    assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
+
+    let mut i = MixedInts::TwoSmallInts(42, 64);
+    assert_eq!(
+        i32::try_from(i),
+        Err("Only SmallInt can be converted to i32")
+    );
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(Ok((42i32, 64i32)), i.try_into());
+    assert_eq!(Ok((&42i32, &64i32)), (&i).try_into());
+    assert_eq!(Ok((&mut 42i32, &mut 64i32)), (&mut i).try_into());
+    assert_eq!(
+        <(i64, i64)>::try_from(i),
+        Err("Only NamedBigInts can be converted to (i64, i64)")
+    );
+    assert_eq!(
+        u32::try_from(i),
+        Err("Only Unsigned, NamedUnsigned can be converted to u32")
+    );
+    assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
+
+    let mut i = MixedInts::NamedBigInts { x: 42, y: 64 };
+    assert_eq!(
+        i32::try_from(i),
+        Err("Only SmallInt can be converted to i32")
+    );
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(
+        <(i32, i32)>::try_from(i),
+        Err("Only TwoSmallInts can be converted to (i32, i32)")
+    );
+    assert_eq!(Ok((42i64, 64i64)), i.try_into());
+    assert_eq!(Ok((&42i64, &64i64)), (&i).try_into());
+    assert_eq!(Ok((&mut 42i64, &mut 64i64)), (&mut i).try_into());
+    assert_eq!(
+        u32::try_from(i),
+        Err("Only Unsigned, NamedUnsigned can be converted to u32")
+    );
+    assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
+
+    let mut i = MixedInts::Unsigned(42);
+    assert_eq!(
+        i32::try_from(i),
+        Err("Only SmallInt can be converted to i32")
+    );
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(
+        <(i32, i32)>::try_from(i),
+        Err("Only TwoSmallInts can be converted to (i32, i32)")
+    );
+    assert_eq!(
+        <(i64, i64)>::try_from(i),
+        Err("Only NamedBigInts can be converted to (i64, i64)")
+    );
+    assert_eq!(Ok(42u32), i.try_into());
+    assert_eq!(Ok(&42u32), (&i).try_into());
+    assert_eq!(Ok(&mut 42u32), (&mut i).try_into());
+    assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
+
+    let mut i = MixedInts::NamedUnsigned { x: 42 };
+    assert_eq!(
+        i32::try_from(i),
+        Err("Only SmallInt can be converted to i32")
+    );
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(
+        <(i32, i32)>::try_from(i),
+        Err("Only TwoSmallInts can be converted to (i32, i32)")
+    );
+    assert_eq!(
+        <(i64, i64)>::try_from(i),
+        Err("Only NamedBigInts can be converted to (i64, i64)")
+    );
+    assert_eq!(Ok(42u32), i.try_into());
+    assert_eq!(Ok(&42u32), (&i).try_into());
+    assert_eq!(Ok(&mut 42u32), (&mut i).try_into());
+    assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
+
+    let i = MixedInts::Unit;
+    assert_eq!(
+        i32::try_from(i),
+        Err("Only SmallInt can be converted to i32")
+    );
+    assert_eq!(
+        i64::try_from(i),
+        Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
+    );
+    assert_eq!(
+        <(i32, i32)>::try_from(i),
+        Err("Only TwoSmallInts can be converted to (i32, i32)")
+    );
+    assert_eq!(
+        <(i64, i64)>::try_from(i),
+        Err("Only NamedBigInts can be converted to (i64, i64)")
+    );
+    assert_eq!(
+        u32::try_from(i),
+        Err("Only Unsigned, NamedUnsigned can be converted to u32")
+    );
+    assert_eq!(Ok(()), i.try_into());
+}
diff --git a/tests/unwrap.rs b/tests/unwrap.rs
new file mode 100644 (file)
index 0000000..ec84f7d
--- /dev/null
@@ -0,0 +1,62 @@
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate derive_more;
+
+#[derive(Unwrap)]
+enum Either<TLeft, TRight> {
+    Left(TLeft),
+    Right(TRight),
+}
+
+#[derive(Unwrap)]
+enum Maybe<T> {
+    Nothing,
+    Just(T),
+}
+
+#[derive(Unwrap)]
+enum Color {
+    RGB(u8, u8, u8),
+    CMYK(u8, u8, u8, u8),
+}
+
+#[derive(Unwrap)]
+enum Nonsense<'a, T> {
+    Ref(&'a T),
+    NoRef,
+    #[unwrap(ignore)]
+    NoRefIgnored,
+}
+
+#[derive(Unwrap)]
+enum WithConstraints<T>
+where
+    T: Copy,
+{
+    One(T),
+    Two,
+}
+#[derive(Unwrap)]
+enum KitchenSink<'a, 'b, T1: Copy, T2: Clone>
+where
+    T2: Into<T1> + 'b,
+{
+    Left(&'a T1),
+    Right(&'b T2),
+    OwnBoth(T1, T2),
+    Empty,
+    NeverMind(),
+    NothingToSeeHere(),
+}
+
+#[test]
+pub fn test_unwrap() {
+    assert_eq!(Maybe::<()>::Nothing.unwrap_nothing(), ());
+}
+
+#[test]
+#[should_panic]
+pub fn test_unwrap_panic() {
+    Maybe::<()>::Nothing.unwrap_just()
+}