diff options
Diffstat (limited to 'third_party/rust/darling')
45 files changed, 2915 insertions, 0 deletions
diff --git a/third_party/rust/darling/.cargo-checksum.json b/third_party/rust/darling/.cargo-checksum.json new file mode 100644 index 0000000000..5b94cd2201 --- /dev/null +++ b/third_party/rust/darling/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"36a132403864fecc7e435509e8339d9f2c0962a451c53357c1d9b08f6d8a36bc","Cargo.lock":"e77a5ba77d6ef12db6c6cdafde228004d938601f46b0bd28a0df7ca836a90142","Cargo.toml":"6ed8f0ac89f171b24a69d4da2c4e8cd711028f0463bd47e9e07026c92f92d16e","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"2147b655029789a691d4b9428c836ee55716bea250fba63b8b1c3b4b280d761c","clippy.toml":"0728f8cee5cdb954e4d5c64c9a84c83ff356a913d531412baf4b5852ba6712ef","compiletests.sh":"a255ae505d79b4a40bdc80b215a5384eadee228593d042fbed60e99cc278e220","examples/automatic_bounds.rs":"2950c8d33bb40f095decba1990d7d0bcd48dffc6e9b7cefce2fcb3818ecf1d18","examples/consume_fields.rs":"ce436936441f1f6734f47074e6625ebf64f15180b9c126a58e24aaa33613b59c","examples/fallible_read.rs":"1e5f2b69e436d899209dc8a4deec0dbe3e998f10a6632a79c0876c46f68267b4","examples/shorthand_or_long_field.rs":"ec2e2b155fd0803c80f98d0ba94e8419dd7307012ccfb916b8f752925a295d55","examples/supports_struct.rs":"08c5cc46400a0e5cf811c0d254a37b42de94b34cd04ac801069105bc792051f6","src/lib.rs":"7fb89b93193dbbab4f32327576fa49d8ba677fc32b09a36f84d3c942b2d7ea06","src/macros_public.rs":"7d2ce0c5026227ef7854db11d7a885ad891255438b2e49bbdfda56fa2f92feec","tests/accrue_errors.rs":"4f0f5be65c5cd639b107a6a14e9fb51573b27cfa9210a2182fa5f450bc1d56db","tests/compile-fail/default_expr_wrong_type.rs":"e09889ac3a243bd6096a0ef7076822b146f1481b70aae703e170ea6527071448","tests/compile-fail/default_expr_wrong_type.stderr":"dce0c05f344be36bf66407b459eb1f6abb560b6f5500d4b771d3fd5babc78325","tests/compile-fail/not_impl_from_meta.rs":"20ef167d88ea06967c26a05234d3b1787f0695d45b4ab1150bf9dc77625f9c74","tests/compile-fail/not_impl_from_meta.stderr":"3e52d37d2a5a6f98e4af70389327f9248ea283aac9bdb0660058ec92e23999b8","tests/compile-fail/skip_field_not_impl_default.rs":"5cb7e2e68d7dc42e6508eb0c170869179f127cf641b44f08f6403760f30c469b","tests/compile-fail/skip_field_not_impl_default.stderr":"664f0c2a5c3c85901b65e923b8907870bead9b2781a4c75497eb6df621a0f701","tests/compiletests.rs":"dc2b570d0e0ab4895d7cd039fa9572d507221f2ff2e546a3dacfd9c64fe1c936","tests/computed_bound.rs":"aed9c00f2b8373e9426337137207e5b9341d236adef682bd2c9427db9ce1d1ff","tests/custom_bound.rs":"9b823063b7fc6c6b3b23905405ce7f316e8a937d5c67c416b89642e537bf9110","tests/defaults.rs":"6894fded24b2f9d173b11f54bbe3b640946c6eb6f5ff5492369e8cd84fc217ef","tests/enums_newtype.rs":"ed63735b88fdfd9037580d878be895a311c13d7d8083ee3077f5ab61e754eb7c","tests/enums_struct.rs":"36ca3d4996644d566b45c78d8c343c4a74fcaa2eba96b4e012c8a1819eb6f4c6","tests/enums_unit.rs":"7f94f793e6adc265c6cdc4b657aa237da1ab0be03b03bce23d3e2180cd3e8d03","tests/error.rs":"f5f84991472e184e1167f0fe8d5f2cbad3844c4022987c9eff46b4db2bcc804a","tests/from_generics.rs":"f2c1f98f654c4e6ad3606d7b12ea7ea66782950c05da132e8d7f2f7e1bdf3af0","tests/from_meta.rs":"b9c73340c44a53a43d7234c6030d37d1ab7c63ea2ec2ae1f7f09e5f2d65baa6b","tests/from_type_param.rs":"94d2766d5ae11d69750497225d6aa3c2f34b09fbc8c3580d61f077d7bb41265b","tests/from_type_param_default.rs":"e00e2f0c779753f66b95e5c0106451f65cbd6fbc28e676381d276290da6254b6","tests/from_variant.rs":"48046b156f6c5d9b3e9c3d0b36b5eebaba1d417447e3babf81ed9c74bee3bfcb","tests/generics.rs":"0c2830acf511148d71ecd4a8b5aa17f80e377aa89f7fe0fc10f6db34671d034a","tests/happy_path.rs":"c7a540fc1755cef757aa5e6cd202a49a47a2040facb0c05c167ec62f8ebbc557","tests/hash_map.rs":"c30764bf1845ca81bc1d752dbe0de965ba70cfbb1571884a20a873ed7bf26360","tests/multiple.rs":"1362ec057f4796ffabf7161033b561b51f069b818af7bac85fe66935c62038dd","tests/newtype.rs":"b5ecf605652b194372cab6d6fef96a2dd4b63ac24649cb52ca944ef9647512ad","tests/skip.rs":"11b5f190d6eac837d4a44a7dedd1ba9e623b0c7a8bf2bdc92882e1f8a8d2aeac","tests/split_declaration.rs":"019863370414af227144aac13272fc39a1e256a9ed0bd3ca2dbf1114f1a9e1ba","tests/suggestions.rs":"e9f8ab55718a5853411a4606f1be1473b57fc7a2789f622d0ed807fcd8ac5606","tests/supports.rs":"fd27b20893a1b1078eff077d426fea7d715b8decd12ad1e0b940cfbfd4fbbfba","tests/unsupported_attributes.rs":"96333cd6602a6f18f47563d5faa923e423b2c02f2ae0d09a15e2d3514593c38d"},"package":"c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8"}
\ No newline at end of file diff --git a/third_party/rust/darling/CHANGELOG.md b/third_party/rust/darling/CHANGELOG.md new file mode 100644 index 0000000000..208ca80bc1 --- /dev/null +++ b/third_party/rust/darling/CHANGELOG.md @@ -0,0 +1,224 @@ +# Changelog + +## v0.14.3 (February 3, 2023) + +- Re-export `syn` from `darling` to avoid requiring that consuming crates have a `syn` dependency. +- Change `<SpannedValue<T> as FromMeta>` impl to more precisely capture the _value_ span, as opposed to the span of the entire item. +- Add `darling::util::{AsShape, Shape, ShapeSet}` to improve "shape" validation for structs and variants. [#222](https://github.com/TedDriggs/issues/222) + +## v0.14.2 (October 26, 2022) + +- Derived impls of `FromMeta` will now error on literals, rather than silently ignoring them. [#193](https://github.com/TedDriggs/darling/pull/193) +- Don't include property paths in compile errors when spans are available. [#203](https://github.com/TedDriggs/darling/pull/203) + +## v0.14.1 (April 28, 2022) + +- Fix a bug where using a trait that accepts `#[darling(attributes(...))]` without specifying any attributes would emit code that did not compile. [#183](https://github.com/TedDriggs/darling/issues/183) +- Impl `Clone` for `darling::Error` [#184](https://github.com/TedDriggs/darling/pull/184) +- Impl `From<darling::Error> for syn::Error` [#184](https://github.com/TedDriggs/darling/pull/184) +- Add `Error::span` and `Error::explicit_span` methods [#184](https://github.com/TedDriggs/darling/pull/184) + +## v0.14.0 (April 13, 2022) + +- **BREAKING CHANGE:** Remove many trait impls from `util::Flag`. + This type had a number of deref and operator impls that made it usable as sort-of-a-boolean. + Real-world usage showed this type is more useful if it's able to carry a span for good errors, + and that most of those impls were unnecessary. [#179](https://github.com/TedDriggs/darling/pull/179) +- Remove need for `#[darling(default)]` on `Option<T>` and `Flag` fields [#161](https://github.com/TedDriggs/darling/issues/161) +- Improve validation of enum shapes [#178](https://github.com/TedDriggs/darling/pull/178) +- Bump `proc-macro2` dependency to 1.0.37 [#180](https://github.com/TedDriggs/darling/pull/180) +- Bump `quote` dependency to 1.0.18 [#180](https://github.com/TedDriggs/darling/pull/180) +- Bump `syn` dependency to 1.0.91 [#180](https://github.com/TedDriggs/darling/pull/180) + +## v0.13.4 (April 6, 2022) + +- Impl `FromMeta` for `syn::Visibility` [#173](https://github.com/TedDriggs/darling/pull/173) + +## v0.13.3 (April 5, 2022) + +- Add `error::Accumulator` for dealing with multiple errors [#164](https://github.com/TedDriggs/darling/pull/164) +- Impl `FromMeta` for `syn::Type` and its variants [#172](https://github.com/TedDriggs/darling/pulls/172) + +## v0.13.2 (March 30, 2022) + +- Impl `FromMeta` for `syn::ExprPath` [#169](https://github.com/TedDriggs/darling/issues/169) + +## v0.13.1 (December 7, 2021) + +- Add `FromAttributes` trait and macro [#151](https://github.com/TedDriggs/darling/issues/151) + +## v0.13.0 (May 20, 2021) + +- Update darling to 2018 edition [#129](https://github.com/TedDriggs/darling/pull/129) +- Error on duplicate fields in `#[darling(...)]` attributes [#130](https://github.com/TedDriggs/darling/pull/130) +- Impl `Copy` for `SpannedValue<T: Copy>` +- Add `SpannedValue::map_ref` + +## v0.13.0-beta (April 20, 2021) + +- Update darling to 2018 edition [#129](https://github.com/TedDriggs/darling/pull/129) +- Error on duplicate fields in `#[darling(...)]` attributes [#130](https://github.com/TedDriggs/darling/pull/130) + +## v0.12.4 (April 20, 2021) + +- Add `and_then` to derive macros for `darling` + +## v0.12.3 (April 8, 2021) + +- Fix `FromMeta` impl for `char` not to panic [#126](https://github.com/TedDriggs/darling/pull/126) + +## v0.12.2 (February 23, 2021) + +- Impl `FromMeta` for `HashMap<Ident, V>` and `HashMap<Path, V>` + +## v0.12.1 (February 22, 2021) + +- Impl `FromMeta` for `syn::ExprArray` [#122](https://github.com/TedDriggs/darling/pull/122) +- Remove use of `unreachable` from `darling::ast::Data` [#123](https://github.com/TedDriggs/darling/pull/123) +- Add `darling::ast::Data::try_empty_from` to avoid panics when trying to read a union body [#123](https://github.com/TedDriggs/darling/pull/123) + +## v0.12.0 (January 5, 2021) + +- POSSIBLY BREAKING: Derived impls of `FromDeriveInput`, `FromField`, `FromVariant`, and `FromTypeParam` will now error when encountering an attribute `darling` has been asked to parse that isn't a supported shape. + Any crates using `darling` that relied on those attributes being silently ignored could see new errors reported in their dependent crates. [#113](https://github.com/TedDriggs/darling/pull/113) +- Impl `syn::spanned::Spanned` for `darling::util::SpannedValue` [#113](https://github.com/TedDriggs/darling/pull/113) +- Add `darling::util::parse_attribute_to_meta_list` to provide useful errors during attribute parsing [#113](https://github.com/TedDriggs/darling/pull/113) +- Add `impl From<syn::Error> for Error` to losslessly propagate `syn` errors [#116](https://github.com/TedDriggs/darling/pull/116) + +## v0.11.0 (December 14, 2020) + +- Bump minor version due to unexpected breaking change [#107](https://github.com/TedDriggs/darling/issues/107) + +## v0.10.3 (December 10, 2020) + +- Add `discriminant` magic field when deriving `FromVariant` [#105](https://github.com/TedDriggs/darling/pull/105) + +## v0.10.2 (October 30, 2019) + +- Bump syn dependency to 1.0.1 [#83](https://github.com/TedDriggs/darling/pull/83) + +## v0.10.1 (September 25, 2019) + +- Fix test compilation errors [#81](https://github.com/TedDriggs/darling/pull/81) + +## v0.10.0 (August 15, 2019) + +- Bump syn and quote to 1.0 [#79](https://github.com/TedDriggs/darling/pull/79) +- Increase rust version to 1.31 + +## v0.9.0 (March 20, 2019) + +- Enable "did you mean" suggestions by default +- Make `darling_core::{codegen, options}` private [#58](https://github.com/TedDriggs/darling/issues/58) +- Fix `Override::as_mut`: [#66](https://github.com/TedDriggs/darling/issues/66) + +## v0.8.6 (March 18, 2019) + +- Added "did you mean" suggestions for unknown fields behind the `suggestions` flag [#60](https://github.com/TedDriggs/issues/60) +- Added `Error::unknown_field_with_alts` to support the suggestion use-case. +- Added `ast::Fields::len` and `ast::Fields::is_empty` methods. + +## v0.8.5 (February 4, 2019) + +- Accept unquoted positive numeric literals [#52](https://github.com/TedDriggs/issues/52) +- Add `FromMeta` to the `syn::Lit` enum and its variants +- Improve error message for unexpected literal formats to not say "other" + +## v0.8.4 (February 4, 2019) + +- Use `syn::Error` to provide precise errors before `proc_macro::Diagnostic` is available +- Add `diagnostics` feature flag to toggle between stable and unstable error backends +- Attach error information in more contexts +- Add `allow_unknown_fields` to support parsing the same attribute multiple times for different macros [#51](https://github.com/darling/issues/51) +- Proc-macro authors will now see better errors in `darling` attributes + +## v0.8.3 (January 21, 2019) + +- Attach spans to errors in generated trait impls [#37](https://github.com/darling/issues/37) +- Attach spans to errors for types with provided bespoke implementations +- Deprecate `set_span` from 0.8.2, as spans should never be broadened after being initially set + +## v0.8.2 (January 17, 2019) + +- Add spans to errors to make quality warnings and errors easy in darling. This is blocked on diagnostics stabilizing. +- Add `darling::util::SpannedValue` so proc-macro authors can remember position information alongside parsed values. + +## v0.8.0 + +- Update dependency on `syn` to 0.15 [#44](https://github.com/darling/pull/44). Thanks to @hcpl + +## v0.7.0 (July 24, 2018) + +- Update dependencies on `syn` and `proc-macro2` +- Add `util::IdentString`, which acts as an Ident or its string equivalent + +## v0.6.3 (May 22, 2018) + +- Add support for `Uses*` traits in where predicates + +## v0.6.2 (May 22, 2018) + +- Add `usage` module for tracking type param and lifetime usage in generic declarations + - Add `UsesTypeParams` and `CollectsTypeParams` traits [#37](https://github.com/darling/issues/37) + - Add `UsesLifetimes` and `CollectLifetimes` traits [#41](https://github.com/darling/pull/41) +- Don't add `FromMeta` bounds to type parameters only used by skipped fields [#40](https://github.com/darling/pull/40) + +## v0.6.1 (May 17, 2018) + +- Fix an issue where the `syn` update broke shape validation [#36](https://github.com/TedDriggs/darling/issues/36) + +## v0.6.0 (May 15, 2018) + +### Breaking Changes + +- Renamed `FromMetaItem` to `FromMeta`, and renamed `from_meta_item` method to `from_meta` +- Added dedicated `derive(FromMetaItem)` which panics and redirects users to `FromMeta` + +## v0.5.0 (May 10, 2018) + +- Add `ast::Generics` and `ast::GenericParam` to work with generics in a manner similar to `ast::Data` +- Add `ast::GenericParamExt` to support alternate representations of generic parameters +- Add `util::WithOriginal` to get a parsed representation and syn's own struct for a syntax block +- Add `FromGenerics` and `FromGenericParam` traits (without derive support) +- Change generated code for `generics` magic field to invoke `FromGenerics` trait during parsing +- Add `FromTypeParam` trait [#30](https://github.com/TedDriggs/darling/pull/30). Thanks to @upsuper + +## v0.4.0 (April 5, 2018) + +- Update dependencies on `proc-macro`, `quote`, and `syn` [#26](https://github.com/TedDriggs/darling/pull/26). Thanks to @hcpl + +## v0.3.3 (April 2, 2018) + +**YANKED** + +## v0.3.2 (March 13, 2018) + +- Derive `Default` on `darling::Ignored` (fixes [#25](https://github.com/TedDriggs/darling/issues/25)). + +## v0.3.1 (March 7, 2018) + +- Support proc-macro2/nightly [#24](https://github.com/TedDriggs/darling/pull/24). Thanks to @kdy1 + +## v0.3.0 (January 26, 2018) + +### Breaking Changes + +- Update `syn` to 0.12 [#20](https://github.com/TedDriggs/darling/pull/20). Thanks to @Eijebong +- Update `quote` to 0.4 [#20](https://github.com/TedDriggs/darling/pull/20). Thanks to @Eijebong +- Rename magic field `body` in derived `FromDeriveInput` structs to `data` to stay in sync with `syn` +- Rename magic field `data` in derived `FromVariant` structs to `fields` to stay in sync with `syn` + +## v0.2.2 (December 5, 2017) + +- Update `lazy_static` to 1.0 [#15](https://github.com/TedDriggs/darling/pull/16). Thanks to @Eijebong + +## v0.2.1 (November 28, 2017) + +- Add `impl FromMetaItem` for integer types [#15](https://github.com/TedDriggs/darling/pull/15) + +## v0.2.0 (June 18, 2017) + +- Added support for returning multiple errors from parsing [#5](https://github.com/TedDriggs/darling/pull/5) +- Derived impls no longer return on first error [#5](https://github.com/TedDriggs/darling/pull/5) +- Removed default types for `V` and `F` from `ast::Body` +- Enum variants are automatically converted to snake_case [#12](https://github.com/TedDriggs/darling/pull/12) diff --git a/third_party/rust/darling/Cargo.lock b/third_party/rust/darling/Cargo.lock new file mode 100644 index 0000000000..f116d65c3f --- /dev/null +++ b/third_party/rust/darling/Cargo.lock @@ -0,0 +1,216 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "darling" +version = "0.14.3" +dependencies = [ + "darling_core", + "darling_macro", + "proc-macro2", + "quote", + "rustversion", + "syn", + "trybuild", +] + +[[package]] +name = "darling_core" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "serde" +version = "1.0.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "trybuild" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db29f438342820400f2d9acfec0d363e987a38b2950bdb50a7069ed17b2148ee" +dependencies = [ + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/third_party/rust/darling/Cargo.toml b/third_party/rust/darling/Cargo.toml new file mode 100644 index 0000000000..68115fdb63 --- /dev/null +++ b/third_party/rust/darling/Cargo.toml @@ -0,0 +1,58 @@ +# 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 = "darling" +version = "0.14.3" +authors = ["Ted Driggs <ted.driggs@outlook.com>"] +exclude = [ + "/.travis.yml", + "/publish.sh", + "/.github/**", +] +description = """ +A proc-macro library for reading attributes into structs when +implementing custom derives. +""" +documentation = "https://docs.rs/darling/0.14.3" +readme = "README.md" +license = "MIT" +repository = "https://github.com/TedDriggs/darling" + +[dependencies.darling_core] +version = "=0.14.3" + +[dependencies.darling_macro] +version = "=0.14.3" + +[dev-dependencies.proc-macro2] +version = "1.0.37" + +[dev-dependencies.quote] +version = "1.0.18" + +[dev-dependencies.syn] +version = "1.0.91" + +[features] +default = ["suggestions"] +diagnostics = ["darling_core/diagnostics"] +suggestions = ["darling_core/suggestions"] + +[target."cfg(compiletests)".dev-dependencies.rustversion] +version = "1.0.9" + +[target."cfg(compiletests)".dev-dependencies.trybuild] +version = "1.0.38" + +[badges.maintenance] +status = "actively-developed" diff --git a/third_party/rust/darling/LICENSE b/third_party/rust/darling/LICENSE new file mode 100644 index 0000000000..0b48eadc9d --- /dev/null +++ b/third_party/rust/darling/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Ted Driggs + +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/third_party/rust/darling/README.md b/third_party/rust/darling/README.md new file mode 100644 index 0000000000..5f7f9d5b21 --- /dev/null +++ b/third_party/rust/darling/README.md @@ -0,0 +1,138 @@ +Darling +======= + +[![Build Status](https://github.com/TedDriggs/darling/workflows/CI/badge.svg)](https://github.com/TedDriggs/darling/actions) +[![Latest Version](https://img.shields.io/crates/v/darling.svg)](https://crates.io/crates/darling) +[![Rustc Version 1.31+](https://img.shields.io/badge/rustc-1.31+-lightgray.svg)](https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html) + +`darling` is a crate for proc macro authors, which enables parsing attributes into structs. It is heavily inspired by `serde` both in its internals and in its API. + +# Benefits +* Easy and declarative parsing of macro input - make your proc-macros highly controllable with minimal time investment. +* Great validation and errors, no work required. When users of your proc-macro make a mistake, `darling` makes sure they get error markers at the right place in their source, and provides "did you mean" suggestions for misspelled fields. + +# Usage +`darling` provides a set of traits which can be derived or manually implemented. + +1. `FromMeta` is used to extract values from a meta-item in an attribute. Implementations are likely reusable for many libraries, much like `FromStr` or `serde::Deserialize`. Trait implementations are provided for primitives, some std types, and some `syn` types. +2. `FromDeriveInput` is implemented or derived by each proc-macro crate which depends on `darling`. This is the root for input parsing; it gets access to the identity, generics, and visibility of the target type, and can specify which attribute names should be parsed or forwarded from the input AST. +3. `FromField` is implemented or derived by each proc-macro crate which depends on `darling`. Structs deriving this trait will get access to the identity (if it exists), type, and visibility of the field. +4. `FromVariant` is implemented or derived by each proc-macro crate which depends on `darling`. Structs deriving this trait will get access to the identity and contents of the variant, which can be transformed the same as any other `darling` input. +5. `FromAttributes` is a lower-level version of the more-specific `FromDeriveInput`, `FromField`, and `FromVariant` traits. Structs deriving this trait get a meta-item extractor and error collection which works for any syntax element, including traits, trait items, and functions. This is useful for non-derive proc macros. + +## Additional Modules +* `darling::ast` provides generic types for representing the AST. +* `darling::usage` provides traits and functions for determining where type parameters and lifetimes are used in a struct or enum. +* `darling::util` provides helper types with special `FromMeta` implementations, such as `IdentList`. + +# Example + +```rust,ignore +#[macro_use] +extern crate darling; +extern crate syn; + +#[derive(Default, FromMeta)] +#[darling(default)] +pub struct Lorem { + #[darling(rename = "sit")] + ipsum: bool, + dolor: Option<String>, +} + +#[derive(FromDeriveInput)] +#[darling(attributes(my_crate), forward_attrs(allow, doc, cfg))] +pub struct MyTraitOpts { + ident: syn::Ident, + attrs: Vec<syn::Attribute>, + lorem: Lorem, +} +``` + +The above code will then be able to parse this input: + +```rust,ignore +/// A doc comment which will be available in `MyTraitOpts::attrs`. +#[derive(MyTrait)] +#[my_crate(lorem(dolor = "Hello", sit))] +pub struct ConsumingType; +``` + +# Attribute Macros +Non-derive attribute macros are supported. +To parse arguments for attribute macros, derive `FromMeta` on the argument receiver type, then pass `&syn::AttributeArgs` to the `from_list` method. +This will produce a normal `darling::Result<T>` that can be used the same as a result from parsing a `DeriveInput`. + +## Macro Code +```rust,ignore +use darling::FromMeta; +use syn::{AttributeArgs, ItemFn}; +use proc_macro::TokenStream; + +#[derive(Debug, FromMeta)] +pub struct MacroArgs { + #[darling(default)] + timeout_ms: Option<u16>, + path: String, +} + +#[proc_macro_attribute] +fn your_attr(args: TokenStream, input: TokenStream) -> TokenStream { + let attr_args = parse_macro_input!(args as AttributeArgs); + let _input = parse_macro_input!(input as ItemFn); + + let _args = match MacroArgs::from_list(&attr_args) { + Ok(v) => v, + Err(e) => { return TokenStream::from(e.write_errors()); } + }; + + // do things with `args` + unimplemented!() +} +``` + +## Consuming Code +```rust,ignore +use your_crate::your_attr; + +#[your_attr(path = "hello", timeout_ms = 15)] +fn do_stuff() { + println!("Hello"); +} +``` + +# Features +Darling's features are built to work well for real-world projects. + +* **Defaults**: Supports struct- and field-level defaults, using the same path syntax as `serde`. + Additionally, `Option<T>` and `darling::util::Flag` fields are innately optional; you don't need to declare `#[darling(default)]` for those. +* **Field Renaming**: Fields can have different names in usage vs. the backing code. +* **Auto-populated fields**: Structs deriving `FromDeriveInput` and `FromField` can declare properties named `ident`, `vis`, `ty`, `attrs`, and `generics` to automatically get copies of the matching values from the input AST. `FromDeriveInput` additionally exposes `data` to get access to the body of the deriving type, and `FromVariant` exposes `fields`. +* **Mapping function**: Use `#[darling(map="path")]` or `#[darling(and_then="path")]` to specify a function that runs on the result of parsing a meta-item field. This can change the return type, which enables you to parse to an intermediate form and convert that to the type you need in your struct. +* **Skip fields**: Use `#[darling(skip)]` to mark a field that shouldn't be read from attribute meta-items. +* **Multiple-occurrence fields**: Use `#[darling(multiple)]` on a `Vec` field to allow that field to appear multiple times in the meta-item. Each occurrence will be pushed into the `Vec`. +* **Span access**: Use `darling::util::SpannedValue` in a struct to get access to that meta item's source code span. This can be used to emit warnings that point at a specific field from your proc macro. In addition, you can use `darling::Error::write_errors` to automatically get precise error location details in most cases. +* **"Did you mean" suggestions**: Compile errors from derived darling trait impls include suggestions for misspelled fields. + +## Shape Validation +Some proc-macros only work on structs, while others need enums whose variants are either unit or newtype variants. +Darling makes this sort of validation extremely simple. +On the receiver that derives `FromDeriveInput`, add `#[darling(supports(...))]` and then list the shapes that your macro should accept. + +|Name|Description| +|---|---| +|`any`|Accept anything| +|`struct_any`|Accept any struct| +|`struct_named`|Accept structs with named fields, e.g. `struct Example { field: String }`| +|`struct_newtype`|Accept newtype structs, e.g. `struct Example(String)`| +|`struct_tuple`|Accept tuple structs, e.g. `struct Example(String, String)`| +|`struct_unit`|Accept unit structs, e.g. `struct Example;`| +|`enum_any`|Accept any enum| +|`enum_named`|Accept enum variants with named fields| +|`enum_newtype`|Accept newtype enum variants| +|`enum_tuple`|Accept tuple enum variants| +|`enum_unit`|Accept unit enum variants| + +Each one is additive, so listing `#[darling(supports(struct_any, enum_newtype))]` would accept all structs and any enum where every variant is a newtype variant. + +This can also be used when deriving `FromVariant`, without the `enum_` prefix.
\ No newline at end of file diff --git a/third_party/rust/darling/clippy.toml b/third_party/rust/darling/clippy.toml new file mode 100644 index 0000000000..ce89b55090 --- /dev/null +++ b/third_party/rust/darling/clippy.toml @@ -0,0 +1,2 @@ +msrv = "1.31.0" +disallowed-names = [] # we want to be able to use placeholder names in tests
\ No newline at end of file diff --git a/third_party/rust/darling/compiletests.sh b/third_party/rust/darling/compiletests.sh new file mode 100755 index 0000000000..a8ce04288d --- /dev/null +++ b/third_party/rust/darling/compiletests.sh @@ -0,0 +1 @@ +RUSTFLAGS="--cfg=compiletests" cargo +1.65.0 test --test compiletests
\ No newline at end of file diff --git a/third_party/rust/darling/examples/automatic_bounds.rs b/third_party/rust/darling/examples/automatic_bounds.rs new file mode 100644 index 0000000000..8312afed4c --- /dev/null +++ b/third_party/rust/darling/examples/automatic_bounds.rs @@ -0,0 +1,73 @@ +use darling::{FromDeriveInput, FromMeta}; + +#[derive(FromMeta, PartialEq, Eq, Debug)] +enum Volume { + Whisper, + Talk, + Shout, +} + +/// A more complex example showing the ability to skip at a field or struct +/// level while still tracking which type parameters need to be bounded. +/// This can be seen by expanding this example using `cargo expand`. +#[derive(FromMeta)] +#[allow(dead_code)] +enum Emphasis<T> { + Constant(Volume), + Variable(darling::util::PathList), + #[darling(skip)] + PerPhoneme(Option<T>), + Strided { + #[darling(skip)] + step: Vec<T>, + #[darling(multiple)] + volume: Vec<Volume>, + }, +} + +#[derive(FromDeriveInput)] +#[darling(attributes(speak))] +struct SpeakingOptions<T, U> { + max_volume: U, + #[darling(skip, default)] + additional_data: Vec<T>, +} + +#[derive(Default)] +struct Phoneme { + #[allow(dead_code)] + first: String, +} + +// This is probably the holy grail for `darling`'s own internal use-case: +// Auto-apply `Default` bound to skipped *field* types in `where` clause. +impl<T, U> Default for SpeakingOptions<T, U> +where + Vec<T>: Default, + U: Default, +{ + fn default() -> Self { + Self { + max_volume: Default::default(), + additional_data: Default::default(), + } + } +} + +fn main() { + let derive_input = syn::parse_str( + r#" + #[derive(Speak)] + #[speak(max_volume = "shout")] + enum HtmlElement { + Div(String) + } + "#, + ) + .unwrap(); + + let parsed: SpeakingOptions<Phoneme, Volume> = + FromDeriveInput::from_derive_input(&derive_input).unwrap(); + assert_eq!(parsed.max_volume, Volume::Shout); + assert_eq!(parsed.additional_data.len(), 0); +} diff --git a/third_party/rust/darling/examples/consume_fields.rs b/third_party/rust/darling/examples/consume_fields.rs new file mode 100644 index 0000000000..f5cd435b90 --- /dev/null +++ b/third_party/rust/darling/examples/consume_fields.rs @@ -0,0 +1,175 @@ +// The use of fields in debug print commands does not count as "used", +// which causes the fields to trigger an unwanted dead code warning. +#![allow(dead_code)] + +//! This example shows how to do struct and field parsing using darling. + +use darling::{ast, FromDeriveInput, FromField, FromMeta}; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::parse_str; + +/// A speaking volume. Deriving `FromMeta` will cause this to be usable +/// as a string value for a meta-item key. +#[derive(Debug, Clone, Copy, FromMeta)] +#[darling(default)] +enum Volume { + Normal, + Whisper, + Shout, +} + +impl Default for Volume { + fn default() -> Self { + Volume::Normal + } +} + +/// Support parsing from a full derive input. Unlike FromMeta, this isn't +/// composable; each darling-dependent crate should have its own struct to handle +/// when its trait is derived. +#[derive(Debug, FromDeriveInput)] +// This line says that we want to process all attributes declared with `my_trait`, +// and that darling should panic if this receiver is given an enum. +#[darling(attributes(my_trait), supports(struct_any))] +struct MyInputReceiver { + /// The struct ident. + ident: syn::Ident, + + /// The type's generics. You'll need these any time your trait is expected + /// to work with types that declare generics. + generics: syn::Generics, + + /// Receives the body of the struct or enum. We don't care about + /// struct fields because we previously told darling we only accept structs. + data: ast::Data<(), MyFieldReceiver>, + + /// The Input Receiver demands a volume, so use `Volume::Normal` if the + /// caller doesn't provide one. + #[darling(default)] + volume: Volume, +} + +impl ToTokens for MyInputReceiver { + fn to_tokens(&self, tokens: &mut TokenStream) { + let MyInputReceiver { + ref ident, + ref generics, + ref data, + volume, + } = *self; + + let (imp, ty, wher) = generics.split_for_impl(); + let fields = data + .as_ref() + .take_struct() + .expect("Should never be enum") + .fields; + + // Generate the format string which shows each field and its name + let fmt_string = fields + .iter() + .enumerate() + .map(|(i, f)| { + // We have to preformat the ident in this case so we can fall back + // to the field index for unnamed fields. It's not easy to read, + // unfortunately. + format!( + "{} = {{}}", + f.ident + .as_ref() + .map(|v| format!("{}", v)) + .unwrap_or_else(|| format!("{}", i)) + ) + }) + .collect::<Vec<_>>() + .join(", "); + + // Generate the actual values to fill the format string. + let field_list = fields + .into_iter() + .enumerate() + .map(|(i, f)| { + let field_volume = f.volume.unwrap_or(volume); + + // This works with named or indexed fields, so we'll fall back to the index so we can + // write the output as a key-value pair. + let field_ident = f.ident + .as_ref() + .map(|v| quote!(#v)) + .unwrap_or_else(|| { + let i = syn::Index::from(i); + quote!(#i) + }); + + match field_volume { + Volume::Normal => quote!(self.#field_ident), + Volume::Shout => { + quote!(::std::string::ToString::to_string(&self.#field_ident).to_uppercase()) + } + Volume::Whisper => { + quote!(::std::string::ToString::to_string(&self.#field_ident).to_lowercase()) + } + } + }) + .collect::<Vec<_>>(); + + tokens.extend(quote! { + impl #imp Speak for #ident #ty #wher { + fn speak(&self, writer: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(writer, #fmt_string, #(#field_list),*) + } + } + }); + } +} + +#[derive(Debug, FromField)] +#[darling(attributes(my_trait))] +struct MyFieldReceiver { + /// Get the ident of the field. For fields in tuple or newtype structs or + /// enum bodies, this can be `None`. + ident: Option<syn::Ident>, + + /// This magic field name pulls the type from the input. + ty: syn::Type, + + /// We declare this as an `Option` so that during tokenization we can write + /// `field.volume.unwrap_or(derive_input.volume)` to facilitate field-level + /// overrides of struct-level settings. + /// + /// Because this field is an `Option`, we don't need to include `#[darling(default)]` + volume: Option<Volume>, +} + +fn main() { + let input = r#"#[derive(MyTrait)] +#[my_trait(volume = "shout")] +pub struct Foo { + #[my_trait(volume = "whisper")] + bar: bool, + + baz: i64, +}"#; + + let parsed = parse_str(input).unwrap(); + let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap(); + let tokens = quote!(#receiver); + + println!( + r#" +INPUT: + +{} + +PARSED AS: + +{:?} + +EMITS: + +{} + "#, + input, receiver, tokens + ); +} diff --git a/third_party/rust/darling/examples/fallible_read.rs b/third_party/rust/darling/examples/fallible_read.rs new file mode 100644 index 0000000000..850465e82d --- /dev/null +++ b/third_party/rust/darling/examples/fallible_read.rs @@ -0,0 +1,85 @@ +//! This example demonstrates techniques for performing custom error handling +//! in a derive-input receiver. +//! +//! 1. Using `darling::Result` as a carrier to preserve the error for later display +//! 1. Using `Result<T, syn::Meta>` to attempt a recovery in imperative code +//! 1. Using the `map` darling meta-item to post-process a field before returning +//! 1. Using the `and_then` darling meta-item to post-process the receiver before returning + +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_str; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(my_trait), and_then = "MyInputReceiver::autocorrect")] +pub struct MyInputReceiver { + /// This field must be present and a string or else parsing will panic. + #[darling(map = "MyInputReceiver::make_string_shouty")] + name: String, + + /// If this field fails to parse, the struct can still be built; the field + /// will contain the error. The consuming struct can then decide if this + /// blocks code generation. If so, panic or fail in `and_then`. + frequency: darling::Result<i64>, + + /// If this field fails to parse, the struct can still be built; the field + /// will contain an `Err` with the original `syn::Meta`. This can be used + /// for alternate parsing attempts before panicking. + amplitude: Result<u64, syn::Meta>, +} + +impl MyInputReceiver { + /// This function will be called by `darling` _after_ it's finished parsing the + /// `name` field but before initializing `name` with the resulting value. It's + /// a good place for transforms that are easiest to express on already-built + /// types. + fn make_string_shouty(s: String) -> String { + s.to_uppercase() + } + + /// This function will be called by `darling` _after_ it's finished parsing the + /// input but before returning to the caller. This is a good place to initialize + /// skipped fields or to perform corrections that don't lend themselves to being + /// done elsewhere. + fn autocorrect(self) -> darling::Result<Self> { + let Self { + name, + frequency, + amplitude, + } = self; + + // Amplitude doesn't have a sign, so if we received a negative number then + // we'll go ahead and make it positive. + let amplitude = match amplitude { + Ok(amp) => amp, + Err(mi) => (i64::from_meta(&mi)?).abs() as u64, + }; + + Ok(Self { + name, + frequency, + amplitude: Ok(amplitude), + }) + } +} + +fn main() { + let input = r#"#[derive(MyTrait)] +#[my_trait(name="Jon", amplitude = "-1", frequency = 1)] +pub struct Foo;"#; + + let parsed = parse_str(input).unwrap(); + let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap(); + + println!( + r#" +INPUT: + +{} + +PARSED AS: + +{:?} + "#, + input, receiver + ); +} diff --git a/third_party/rust/darling/examples/shorthand_or_long_field.rs b/third_party/rust/darling/examples/shorthand_or_long_field.rs new file mode 100644 index 0000000000..750d83efbc --- /dev/null +++ b/third_party/rust/darling/examples/shorthand_or_long_field.rs @@ -0,0 +1,79 @@ +//! Example showing potentially-nested meta item parsing with `darling::util::Override`. +//! +//! Based on https://stackoverflow.com/q/68046070/86381 by https://github.com/peterjoel + +// The use of fields in debug print commands does not count as "used", +// which causes the fields to trigger an unwanted dead code warning. +#![allow(dead_code)] + +use std::borrow::Cow; + +use darling::{util::Override, FromDeriveInput, FromMeta}; +use syn::{Ident, Path}; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(myderive))] +struct MyDeriveInput { + ident: Ident, + /// We can infer the right "table" behavior for this derive, but we want the caller to be + /// explicit that they're expecting the inference behavior to avoid cluttering some hypothetical + /// database. Therefore this field is required, but can take word form or key-value form. + /// + /// To make this field optional, we could add `#[darling(default)]`, or we could + /// wrap it in `Option` if the presence or absence of the word makes a difference. + table: Override<Table>, +} + +impl MyDeriveInput { + fn table(&self) -> Cow<'_, Table> { + match &self.table { + Override::Explicit(value) => Cow::Borrowed(value), + Override::Inherit => Cow::Owned(Table { + name: self.ident.to_string(), + value: None, + }), + } + } +} + +#[derive(Debug, Clone, FromMeta)] +struct Table { + name: String, + value: Option<Path>, +} + +fn from_str(s: &str) -> darling::Result<MyDeriveInput> { + FromDeriveInput::from_derive_input(&syn::parse_str(s)?) +} + +fn main() { + let missing = from_str( + r#" + #[derive(MyTrait)] + struct Foo(u64); + "#, + ) + .unwrap_err(); + + let short_form = from_str( + r#" + #[derive(MyTrait)] + #[myderive(table)] + struct Foo(u64); + "#, + ) + .unwrap(); + + let long_form = from_str( + r#" + #[derive(MyTrait)] + #[myderive(table(name = "Custom"))] + struct Foo(u64); + "#, + ) + .unwrap(); + + println!("Error when missing: {}", missing); + println!("Short form: {:?}", short_form.table()); + println!("Long form: {:?}", long_form.table()); +} diff --git a/third_party/rust/darling/examples/supports_struct.rs b/third_party/rust/darling/examples/supports_struct.rs new file mode 100644 index 0000000000..97a5bb9c05 --- /dev/null +++ b/third_party/rust/darling/examples/supports_struct.rs @@ -0,0 +1,61 @@ +// The use of fields in debug print commands does not count as "used", +// which causes the fields to trigger an unwanted dead code warning. +#![allow(dead_code)] + +use darling::{ast, util, FromDeriveInput, FromField}; +use syn::{Ident, Type}; + +#[derive(Debug, FromField)] +#[darling(attributes(lorem))] +pub struct LoremField { + ident: Option<Ident>, + ty: Type, + #[darling(default)] + skip: bool, +} + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(lorem), supports(struct_named))] +pub struct Lorem { + ident: Ident, + data: ast::Data<util::Ignored, LoremField>, +} + +fn main() { + let good_input = r#"#[derive(Lorem)] +pub struct Foo { + #[lorem(skip)] + bar: bool, + + baz: i64, +}"#; + + let bad_input = r#"#[derive(Lorem)] + pub struct BadFoo(String, u32);"#; + + let parsed = syn::parse_str(good_input).unwrap(); + let receiver = Lorem::from_derive_input(&parsed).unwrap(); + let wrong_shape_parsed = syn::parse_str(bad_input).unwrap(); + let wrong_shape = Lorem::from_derive_input(&wrong_shape_parsed).expect_err("Shape was wrong"); + + println!( + r#" +INPUT: + +{} + +PARSED AS: + +{:?} + +BAD INPUT: + +{} + +PRODUCED ERROR: + +{} + "#, + good_input, receiver, bad_input, wrong_shape + ); +} diff --git a/third_party/rust/darling/src/lib.rs b/third_party/rust/darling/src/lib.rs new file mode 100644 index 0000000000..b2be36d0cb --- /dev/null +++ b/third_party/rust/darling/src/lib.rs @@ -0,0 +1,108 @@ +//! # Darling +//! Darling is a tool for declarative attribute parsing in proc macro implementations. +//! +//! +//! ## Design +//! Darling takes considerable design inspiration from [`serde`](https://serde.rs). A data structure that can be +//! read from any attribute implements `FromMeta` (or has an implementation automatically +//! generated using `derive`). Any crate can provide `FromMeta` implementations, even one not +//! specifically geared towards proc-macro authors. +//! +//! Proc-macro crates should provide their own structs which implement or derive `FromDeriveInput`, +//! `FromField`, `FromVariant`, `FromGenerics`, _et alia_ to gather settings relevant to their operation. +//! +//! ## Attributes +//! There are a number of attributes that `darling` exposes to enable finer-grained control over the code +//! it generates. +//! +//! * **Field renaming**: You can use `#[darling(rename="new_name")]` on a field to change the name Darling looks for. +//! You can also use `#[darling(rename_all="...")]` at the struct or enum level to apply a casing rule to all fields or variants. +//! * **Map function**: You can use `#[darling(map="path::to::function")]` to run code on a field before its stored in the struct. +//! * **Default values**: You can use `#[darling(default)]` at the type or field level to use that type's default value to fill +//! in values not specified by the caller. +//! * **Skipped fields**: You can skip a variant or field using `#[darling(skip)]`. Fields marked with this will fall back to +//! `Default::default()` for their value, but you can override that with an explicit default or a value from the type-level default. +//! +//! ## Forwarded Fields +//! All derivable traits except `FromMeta` support forwarding some fields from the input AST to the derived struct. +//! These fields are matched up by identifier **before** `rename` attribute values are considered, +//! allowing you to use their names for your own properties. +//! The deriving struct is responsible for making sure the types of fields it chooses to declare are compatible with this table. +//! +//! A deriving struct is free to include or exclude any of the fields below. +//! +//! ### `FromDeriveInput` +//! |Field name|Type|Meaning| +//! |---|---|---| +//! |`ident`|`syn::Ident`|The identifier of the passed-in type| +//! |`vis`|`syn::Visibility`|The visibility of the passed-in type| +//! |`generics`|`T: darling::FromGenerics`|The generics of the passed-in type. This can be `syn::Generics`, `darling::ast::Generics`, or any compatible type.| +//! |`data`|`darling::ast::Data`|The body of the passed-in type| +//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in type. These are controlled using the `forward_attrs` attribute.| +//! +//! ### `FromField` +//! |Field name|Type|Meaning| +//! |---|---|---| +//! |`ident`|`Option<syn::Ident>`|The identifier of the passed-in field, or `None` for tuple fields| +//! |`vis`|`syn::Visibility`|The visibility of the passed-in field| +//! |`ty`|`syn::Type`|The type of the passed-in field| +//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in field. These are controlled using the `forward_attrs` attribute.| +//! +//! ### `FromTypeParam` +//! |Field name|Type|Meaning| +//! |---|---|---| +//! |`ident`|`syn::Ident`|The identifier of the passed-in type param| +//! |`bounds`|`Vec<syn::TypeParamBound>`|The bounds applied to the type param| +//! |`default`|`Option<syn::Type>`|The default type of the parameter, if one exists| +//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in type param. These are controlled using the `forward_attrs` attribute.| +//! +//! ### `FromVariant` +//! |Field name|Type|Meaning| +//! |---|---|---| +//! |`ident`|`syn::Ident`|The identifier of the passed-in variant| +//! |`discriminant`|`Option<syn::Expr>`|For a variant such as `Example = 2`, the `2`| +//! |`fields`|`darling::ast::Fields<T> where T: FromField`|The fields associated with the variant| +//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in variant. These are controlled using the `forward_attrs` attribute.| + +extern crate core; + +#[allow(unused_imports)] +#[macro_use] +extern crate darling_macro; + +#[doc(hidden)] +pub use darling_macro::*; + +#[doc(inline)] +pub use darling_core::{ + FromAttributes, FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, + FromTypeParam, FromVariant, +}; + +#[doc(inline)] +pub use darling_core::{Error, Result}; + +#[doc(inline)] +pub use darling_core::{ast, error, usage, util}; + +// XXX exported so that `ExtractAttribute::extractor` can convert a path into tokens. +// This is likely to change in the future, so only generated code should depend on this export. +#[doc(hidden)] +pub use darling_core::ToTokens; + +/// Core/std trait re-exports. This should help produce generated code which doesn't +/// depend on `std` unnecessarily, and avoids problems caused by aliasing `std` or any +/// of the referenced types. +#[doc(hidden)] +pub mod export { + pub use core::convert::From; + pub use core::default::Default; + pub use core::option::Option::{self, None, Some}; + pub use core::result::Result::{self, Err, Ok}; + pub use darling_core::syn; + pub use std::string::ToString; + pub use std::vec::Vec; +} + +#[macro_use] +mod macros_public; diff --git a/third_party/rust/darling/src/macros_public.rs b/third_party/rust/darling/src/macros_public.rs new file mode 100644 index 0000000000..c264fcc2a8 --- /dev/null +++ b/third_party/rust/darling/src/macros_public.rs @@ -0,0 +1,96 @@ +//! Macros that should be exported from both `darling_core` and `darling`. +//! Note that these are **sym-linked** into the main code, and so cannot declare on items that are exported differently +//! in `darling_core` vs. `darling`. + +/// Generator for `UsesTypeParam` impls that unions the used type parameters of the selected fields. +/// +/// # Usage +/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of +/// fields for the rest of its arguments. +/// +/// The type of each passed-in field must implement `UsesTypeParams`, or the resulting code won't compile. +/// +/// ```rust +/// # extern crate syn; +/// # use darling_core::uses_type_params; +/// # +/// struct MyField { +/// ty: syn::Type, +/// } +/// +/// uses_type_params!(MyField, ty); +/// +/// fn main() { +/// // no test run +/// } +/// ``` +/// +/// `darling` cannot derive this trait automatically, as it doesn't know which information extracted from +/// proc-macro input is meant to constitute "using" the type parameter, but crate consumers should +/// implement it by hand or using the macro. +#[macro_export] +macro_rules! uses_type_params { + ($impl_type:ty, $accessor:ident) => { + impl $crate::usage::UsesTypeParams for $impl_type { + fn uses_type_params<'gen>( + &self, + options: &$crate::usage::Options, + type_set: &'gen $crate::usage::IdentSet + ) -> $crate::usage::IdentRefSet<'gen> { + self.$accessor.uses_type_params(options, type_set) + } + } + }; + ($impl_type:ty, $first:ident, $($field:ident),+) => { + impl $crate::usage::UsesTypeParams for $impl_type { + fn uses_type_params<'gen>( + &self, + options: &$crate::usage::Options, + type_set: &'gen $crate::usage::IdentSet + ) -> $crate::usage::IdentRefSet<'gen> { + let mut hits = self.$first.uses_type_params(options, type_set); + $( + hits.extend(self.$field.uses_type_params(options, type_set)); + )* + hits + } + } + }; +} + +/// Generator for `UsesLifetimes` impls that unions the used lifetimes of the selected fields. +/// +/// # Usage +/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of +/// fields for the rest of its arguments. +/// +/// The type of each passed-in field must implement `UsesLifetimes`, or the resulting code won't compile. +#[macro_export] +macro_rules! uses_lifetimes { + ($impl_type:ty, $accessor:ident) => { + impl $crate::usage::UsesLifetimes for $impl_type { + fn uses_lifetimes<'gen>( + &self, + options: &$crate::usage::Options, + type_set: &'gen $crate::usage::LifetimeSet + ) -> $crate::usage::LifetimeRefSet<'gen> { + self.$accessor.uses_lifetimes(options, type_set) + } + } + }; + ($impl_type:ty, $first:ident, $($field:ident),+) => { + impl $crate::usage::UsesLifetimes for $impl_type { + fn uses_lifetimes<'gen>( + &self, + options: &$crate::usage::Options, + type_set: &'gen $crate::usage::LifetimeSet + ) -> $crate::usage::LifetimeRefSet<'gen> { + let mut hits = self.$first.uses_lifetimes(options, type_set); + $( + hits.extend(self.$field.uses_lifetimes(options, type_set)); + )* + hits + } + } + }; +} diff --git a/third_party/rust/darling/tests/accrue_errors.rs b/third_party/rust/darling/tests/accrue_errors.rs new file mode 100644 index 0000000000..e8799c43cb --- /dev/null +++ b/third_party/rust/darling/tests/accrue_errors.rs @@ -0,0 +1,102 @@ +#![allow(dead_code)] +//! These tests verify that multiple errors will be collected up from throughout +//! the parsing process and returned correctly to the caller. + +use darling::{ast, FromDeriveInput, FromField, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(accrue))] +struct Lorem { + ipsum: String, + dolor: Dolor, + data: ast::Data<(), LoremField>, +} + +#[derive(Debug, FromMeta)] +struct Dolor { + sit: bool, +} + +#[derive(Debug, FromField)] +#[darling(attributes(accrue))] +struct LoremField { + ident: Option<syn::Ident>, + aliased_as: syn::Ident, +} + +#[test] +fn bad_type_and_missing_fields() { + let input = parse_quote! { + #[accrue(ipsum = true, dolor(amet = "Hi"))] + pub struct NonConforming { + foo: () + } + }; + + let s_result: ::darling::Error = Lorem::from_derive_input(&input).unwrap_err(); + let err = s_result.flatten(); + println!("{}", err); + assert_eq!(3, err.len()); +} + +#[test] +fn body_only_issues() { + let input = parse_quote! { + #[accrue(ipsum = "Hello", dolor(sit))] + pub struct NonConforming { + foo: (), + bar: bool, + } + }; + + let s_err = Lorem::from_derive_input(&input).unwrap_err(); + println!("{:?}", s_err); + assert_eq!(2, s_err.len()); +} + +#[derive(Debug, FromMeta)] +enum Week { + Monday, + Tuesday { morning: bool, afternoon: String }, + Wednesday(Dolor), +} + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(accrue))] +struct Month { + schedule: Week, +} + +#[test] +fn error_in_enum_fields() { + let input = parse_quote! { + #[accrue(schedule(tuesday(morning = "yes")))] + pub struct NonConforming { + foo: (), + bar: bool, + } + }; + + let s_err = Month::from_derive_input(&input).unwrap_err(); + assert_eq!(2, s_err.len()); + let err = s_err.flatten(); + // TODO add tests to check location path is correct + println!("{}", err); +} + +#[test] +fn error_in_newtype_variant() { + let input = parse_quote! { + #[accrue(schedule(wednesday(sit = "yes")))] + pub struct NonConforming { + foo: (), + bar: bool, + } + }; + + let s_err = Month::from_derive_input(&input).unwrap_err(); + assert_eq!(1, s_err.len()); + println!("{}", s_err); + println!("{}", s_err.flatten()); +} diff --git a/third_party/rust/darling/tests/compile-fail/default_expr_wrong_type.rs b/third_party/rust/darling/tests/compile-fail/default_expr_wrong_type.rs new file mode 100644 index 0000000000..cd9a0fd1ee --- /dev/null +++ b/third_party/rust/darling/tests/compile-fail/default_expr_wrong_type.rs @@ -0,0 +1,12 @@ +use darling::FromMeta; + +#[derive(FromMeta)] +struct Receiver { + #[darling(default = "usize::default")] + not_u32: String, + + #[darling(multiple, default = "usize::default")] + also_not_u32: Vec<String>, +} + +fn main() {} diff --git a/third_party/rust/darling/tests/compile-fail/default_expr_wrong_type.stderr b/third_party/rust/darling/tests/compile-fail/default_expr_wrong_type.stderr new file mode 100644 index 0000000000..a8b7c502bf --- /dev/null +++ b/third_party/rust/darling/tests/compile-fail/default_expr_wrong_type.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> tests/compile-fail/default_expr_wrong_type.rs:5:25 + | +5 | #[darling(default = "usize::default")] + | ^^^^^^^^^^^^^^^^ expected struct `String`, found `usize` + | +help: try using a conversion method + | +5 | #[darling(default = "usize::default".to_string())] + | ++++++++++++ +5 | #[darling(default = "usize::default".to_string())] + | ++++++++++++ + +error[E0308]: mismatched types + --> tests/compile-fail/default_expr_wrong_type.rs:8:35 + | +8 | #[darling(multiple, default = "usize::default")] + | ^^^^^^^^^^^^^^^^ expected struct `Vec`, found `usize` + | + = note: expected struct `Vec<String>` + found type `usize` diff --git a/third_party/rust/darling/tests/compile-fail/not_impl_from_meta.rs b/third_party/rust/darling/tests/compile-fail/not_impl_from_meta.rs new file mode 100644 index 0000000000..f27d716391 --- /dev/null +++ b/third_party/rust/darling/tests/compile-fail/not_impl_from_meta.rs @@ -0,0 +1,16 @@ +use darling::FromMeta; + +struct NotImplFm; + +#[derive(FromMeta)] +struct OuterFm { + inner: NotImplFm, +} + +#[derive(darling::FromDeriveInput)] +#[darling(attributes(hello))] +struct OuterFdi { + inner: NotImplFm, +} + +fn main() {} diff --git a/third_party/rust/darling/tests/compile-fail/not_impl_from_meta.stderr b/third_party/rust/darling/tests/compile-fail/not_impl_from_meta.stderr new file mode 100644 index 0000000000..a166a6fcff --- /dev/null +++ b/third_party/rust/darling/tests/compile-fail/not_impl_from_meta.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NotImplFm: FromMeta` is not satisfied + --> tests/compile-fail/not_impl_from_meta.rs:7:12 + | +7 | inner: NotImplFm, + | ^^^^^^^^^ the trait `FromMeta` is not implemented for `NotImplFm` + | + = help: the following other types implement trait `FromMeta`: + () + Arc<T> + AtomicBool + ExprArray + ExprPath + Flag + HashMap<String, V, S> + HashMap<proc_macro2::Ident, V, S> + and $N others + +error[E0277]: the trait bound `NotImplFm: FromMeta` is not satisfied + --> tests/compile-fail/not_impl_from_meta.rs:13:12 + | +13 | inner: NotImplFm, + | ^^^^^^^^^ the trait `FromMeta` is not implemented for `NotImplFm` + | + = help: the following other types implement trait `FromMeta`: + () + Arc<T> + AtomicBool + ExprArray + ExprPath + Flag + HashMap<String, V, S> + HashMap<proc_macro2::Ident, V, S> + and $N others diff --git a/third_party/rust/darling/tests/compile-fail/skip_field_not_impl_default.rs b/third_party/rust/darling/tests/compile-fail/skip_field_not_impl_default.rs new file mode 100644 index 0000000000..f0d44c7798 --- /dev/null +++ b/third_party/rust/darling/tests/compile-fail/skip_field_not_impl_default.rs @@ -0,0 +1,18 @@ +use darling::FromMeta; + +#[derive(FromMeta)] +struct NoDefault(String); + +#[derive(FromMeta)] +struct Recevier { + #[darling(skip)] + skipped: NoDefault, + + #[darling(skip = true)] + explicitly_skipped: NoDefault, + + #[darling(skip = false)] + not_skipped_no_problem: NoDefault, +} + +fn main() {} diff --git a/third_party/rust/darling/tests/compile-fail/skip_field_not_impl_default.stderr b/third_party/rust/darling/tests/compile-fail/skip_field_not_impl_default.stderr new file mode 100644 index 0000000000..de46982c24 --- /dev/null +++ b/third_party/rust/darling/tests/compile-fail/skip_field_not_impl_default.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `NoDefault: std::default::Default` is not satisfied + --> tests/compile-fail/skip_field_not_impl_default.rs:8:15 + | +8 | #[darling(skip)] + | ^^^^ the trait `std::default::Default` is not implemented for `NoDefault` + | +help: consider annotating `NoDefault` with `#[derive(Default)]` + | +4 | #[derive(Default)] + | + +error[E0277]: the trait bound `NoDefault: std::default::Default` is not satisfied + --> tests/compile-fail/skip_field_not_impl_default.rs:11:22 + | +11 | #[darling(skip = true)] + | ^^^^ the trait `std::default::Default` is not implemented for `NoDefault` + | +help: consider annotating `NoDefault` with `#[derive(Default)]` + | +4 | #[derive(Default)] + | diff --git a/third_party/rust/darling/tests/compiletests.rs b/third_party/rust/darling/tests/compiletests.rs new file mode 100644 index 0000000000..00a5b3296f --- /dev/null +++ b/third_party/rust/darling/tests/compiletests.rs @@ -0,0 +1,16 @@ +#![cfg(compiletests)] + +#[rustversion::stable(1.65)] +#[test] +fn compile_test() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile-fail/*.rs"); +} + +#[rustversion::not(stable(1.65))] +#[test] +fn wrong_rustc_version() { + panic!( + "This is not the expected version of rustc. Error messages vary across compiler versions so tests may produce spurious errors" + ); +} diff --git a/third_party/rust/darling/tests/computed_bound.rs b/third_party/rust/darling/tests/computed_bound.rs new file mode 100644 index 0000000000..abdb1022c1 --- /dev/null +++ b/third_party/rust/darling/tests/computed_bound.rs @@ -0,0 +1,42 @@ +use darling::{FromDeriveInput, FromMeta}; + +fn parse<T: FromDeriveInput>(src: &str) -> T { + let ast = syn::parse_str(src).unwrap(); + FromDeriveInput::from_derive_input(&ast).unwrap() +} + +#[derive(FromMeta, PartialEq, Eq, Debug)] +enum Volume { + Whisper, + Talk, + Shout, +} + +#[derive(FromDeriveInput)] +#[darling(attributes(speak))] +struct SpeakingOptions<T: Default, U> { + max_volume: U, + #[darling(skip)] + #[allow(dead_code)] + additional_data: T, +} + +#[derive(Default)] +struct Phoneme { + #[allow(dead_code)] + first: String, +} + +#[test] +fn skipped_field() { + let parsed: SpeakingOptions<Phoneme, Volume> = parse( + r#" + #[derive(Speak)] + #[speak(max_volume = "shout")] + enum HtmlElement { + Div(String) + } + "#, + ); + assert_eq!(parsed.max_volume, Volume::Shout); +} diff --git a/third_party/rust/darling/tests/custom_bound.rs b/third_party/rust/darling/tests/custom_bound.rs new file mode 100644 index 0000000000..312f147805 --- /dev/null +++ b/third_party/rust/darling/tests/custom_bound.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] + +use std::ops::Add; + +use darling::{FromDeriveInput, FromMeta}; + +#[derive(Debug, Clone, FromMeta)] +#[darling(bound = "T: FromMeta + Add")] +struct Wrapper<T>(pub T); + +impl<T: Add> Add for Wrapper<T> { + type Output = Wrapper<<T as Add>::Output>; + fn add(self, rhs: Self) -> Wrapper<<T as Add>::Output> { + Wrapper(self.0 + rhs.0) + } +} + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(hello), bound = "Wrapper<T>: Add, T: FromMeta")] +struct Foo<T> { + lorem: Wrapper<T>, +} + +#[test] +fn expansion() {} diff --git a/third_party/rust/darling/tests/defaults.rs b/third_party/rust/darling/tests/defaults.rs new file mode 100644 index 0000000000..05ab1ed1db --- /dev/null +++ b/third_party/rust/darling/tests/defaults.rs @@ -0,0 +1,189 @@ +use darling::FromDeriveInput; +use syn::parse_quote; + +mod foo { + pub mod bar { + pub fn init() -> String { + String::from("hello") + } + } +} + +#[derive(FromDeriveInput)] +#[darling(attributes(speak))] +pub struct SpeakerOpts { + #[darling(default = "foo::bar::init")] + first_word: String, +} + +#[test] +fn path_default() { + let speaker: SpeakerOpts = FromDeriveInput::from_derive_input(&parse_quote! { + struct Foo; + }) + .expect("Unit struct with no attrs should parse"); + + assert_eq!(speaker.first_word, "hello"); +} + +/// Tests in this module capture the somewhat-confusing behavior observed when defaults +/// are set at both the field and container level. +/// +/// The general rule is that more-specific declarations preempt less-specific ones; this is +/// unsurprising and allows for granular control over what happens when parsing an AST. +mod stacked_defaults { + use darling::{FromDeriveInput, FromMeta}; + use syn::parse_quote; + + fn jane() -> String { + "Jane".into() + } + + #[derive(FromMeta)] + #[darling(default)] + struct PersonName { + #[darling(default = "jane")] + first: String, + #[darling(default)] + middle: String, + last: String, + } + + impl Default for PersonName { + fn default() -> Self { + Self { + first: "John".into(), + middle: "T".into(), + last: "Doe".into(), + } + } + } + + #[derive(FromDeriveInput)] + #[darling(attributes(person))] + struct Person { + #[darling(default)] + name: PersonName, + age: u8, + } + + #[test] + fn name_first_only() { + let person = Person::from_derive_input(&parse_quote! { + #[person(name(first = "Bill"), age = 5)] + struct Foo; + }) + .unwrap(); + + assert_eq!(person.name.first, "Bill"); + assert_eq!( + person.name.middle, "", + "Explicit field-level default should preempt container-level default" + ); + assert_eq!( + person.name.last, "Doe", + "Absence of a field-level default falls back to container-level default" + ); + } + + /// This is the most surprising case. The presence of `name()` means we invoke + /// `PersonName::from_list(&[])`. When that finishes parsing each of the zero nested + /// items it has received, it will then start filling in missing fields, using the + /// explicit field-level defaults for `first` and `middle`, while for `last` it will + /// use the `last` field from the container-level default. + #[test] + fn name_empty_list() { + let person = Person::from_derive_input(&parse_quote! { + #[person(name(), age = 5)] + struct Foo; + }) + .unwrap(); + + assert_eq!(person.name.first, "Jane"); + assert_eq!(person.name.middle, ""); + assert_eq!(person.name.last, "Doe"); + } + + #[test] + fn no_name() { + let person = Person::from_derive_input(&parse_quote! { + #[person(age = 5)] + struct Foo; + }) + .unwrap(); + + assert_eq!(person.age, 5); + assert_eq!( + person.name.first, "John", + "If `name` is not specified, `Person`'s field-level default should be used" + ); + assert_eq!(person.name.middle, "T"); + assert_eq!(person.name.last, "Doe"); + } +} + +mod implicit_default { + use darling::{util::Flag, FromDeriveInput}; + use syn::parse_quote; + + // No use of `darling(default)` here at all! + // This struct will fill in missing fields using FromMeta::from_none. + #[derive(FromDeriveInput)] + #[darling(attributes(person))] + struct Person { + first_name: String, + last_name: Option<String>, + lefty: Flag, + } + + #[test] + fn missing_fields_fill() { + let person = Person::from_derive_input(&parse_quote! { + #[person(first_name = "James")] + struct Foo; + }) + .unwrap(); + + assert_eq!(person.first_name, "James"); + assert_eq!(person.last_name, None); + assert!(!person.lefty.is_present()); + } +} + +/// Test that a field-level implicit default using FromMeta::from_none is superseded +/// by the parent declaring `#[darling(default)]`. +mod overridden_implicit_default { + use darling::{util::Flag, FromDeriveInput}; + use syn::parse_quote; + + #[derive(FromDeriveInput)] + #[darling(default, attributes(person))] + struct Person { + first_name: String, + last_name: Option<String>, + lefty: Flag, + } + + impl Default for Person { + fn default() -> Self { + Self { + first_name: "Jane".into(), + last_name: Some("Doe".into()), + lefty: Flag::default(), + } + } + } + + #[test] + fn fill_missing() { + let person = Person::from_derive_input(&parse_quote!( + #[person(last_name = "Archer")] + struct Foo; + )) + .unwrap(); + + assert_eq!(person.first_name, "Jane"); + assert_eq!(person.last_name, Some("Archer".into())); + assert!(!person.lefty.is_present()); + } +} diff --git a/third_party/rust/darling/tests/enums_newtype.rs b/third_party/rust/darling/tests/enums_newtype.rs new file mode 100644 index 0000000000..c2c4ec933f --- /dev/null +++ b/third_party/rust/darling/tests/enums_newtype.rs @@ -0,0 +1,90 @@ +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, Default, PartialEq, Eq, FromMeta)] +#[darling(default)] +pub struct Amet { + hello: bool, + world: String, +} + +#[derive(Debug, PartialEq, Eq, FromMeta)] +#[darling(rename_all = "snake_case")] +pub enum Lorem { + Ipsum(bool), + Dolor(String), + Sit(Amet), +} + +#[derive(Debug, PartialEq, Eq, FromDeriveInput)] +#[darling(attributes(hello))] +pub struct Holder { + lorem: Lorem, +} + +impl PartialEq<Lorem> for Holder { + fn eq(&self, other: &Lorem) -> bool { + self.lorem == *other + } +} + +#[test] +fn bool_word() { + let di = parse_quote! { + #[hello(lorem(ipsum))] + pub struct Bar; + }; + + let pr = Holder::from_derive_input(&di).unwrap(); + assert_eq!(pr, Lorem::Ipsum(true)); +} + +#[test] +fn bool_literal() { + let di = parse_quote! { + #[hello(lorem(ipsum = false))] + pub struct Bar; + }; + + let pr = Holder::from_derive_input(&di).unwrap(); + assert_eq!(pr, Lorem::Ipsum(false)); +} + +#[test] +fn string_literal() { + let di = parse_quote! { + #[hello(lorem(dolor = "Hello"))] + pub struct Bar; + }; + + let pr = Holder::from_derive_input(&di).unwrap(); + assert_eq!(pr, Lorem::Dolor("Hello".to_string())); +} + +#[test] +fn struct_nested() { + let di = parse_quote! { + #[hello(lorem(sit(world = "Hello", hello = false)))] + pub struct Bar; + }; + + let pr = Holder::from_derive_input(&di).unwrap(); + assert_eq!( + pr, + Lorem::Sit(Amet { + hello: false, + world: "Hello".to_string(), + }) + ); +} + +#[test] +#[should_panic] +fn format_mismatch() { + let di = parse_quote! { + #[hello(lorem(dolor(world = "Hello", hello = false)))] + pub struct Bar; + }; + + Holder::from_derive_input(&di).unwrap(); +} diff --git a/third_party/rust/darling/tests/enums_struct.rs b/third_party/rust/darling/tests/enums_struct.rs new file mode 100644 index 0000000000..cae4cd5cdc --- /dev/null +++ b/third_party/rust/darling/tests/enums_struct.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] + +//! Test expansion of enums which have struct variants. + +use darling::FromMeta; +#[derive(Debug, FromMeta)] +#[darling(rename_all = "snake_case")] +enum Message { + Hello { user: String, silent: bool }, + Ping, + Goodbye { user: String }, +} + +#[test] +fn expansion() {} diff --git a/third_party/rust/darling/tests/enums_unit.rs b/third_party/rust/darling/tests/enums_unit.rs new file mode 100644 index 0000000000..34e0135657 --- /dev/null +++ b/third_party/rust/darling/tests/enums_unit.rs @@ -0,0 +1,14 @@ +//! Test expansion of enum variants which have no associated data. + +use darling::FromMeta; + +#[derive(Debug, FromMeta)] +#[darling(rename_all = "snake_case")] +enum Pattern { + Owned, + Immutable, + Mutable, +} + +#[test] +fn expansion() {} diff --git a/third_party/rust/darling/tests/error.rs b/third_party/rust/darling/tests/error.rs new file mode 100644 index 0000000000..7274e40894 --- /dev/null +++ b/third_party/rust/darling/tests/error.rs @@ -0,0 +1,54 @@ +//! In case of bad input, parsing should fail. The error should have locations set in derived implementations. + +// The use of fields in debug print commands does not count as "used", +// which causes the fields to trigger an unwanted dead code warning. +#![allow(dead_code)] + +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, FromMeta)] +struct Dolor { + #[darling(rename = "amet")] + sit: bool, + world: bool, +} + +#[derive(Debug, FromDeriveInput)] +#[darling(from_ident, attributes(hello))] +struct Lorem { + ident: syn::Ident, + ipsum: Dolor, +} + +impl From<syn::Ident> for Lorem { + fn from(ident: syn::Ident) -> Self { + Lorem { + ident, + ipsum: Dolor { + sit: false, + world: true, + }, + } + } +} + +#[test] +fn parsing_fail() { + let di = parse_quote! { + #[hello(ipsum(amet = "yes", world = false))] + pub struct Foo; + }; + + println!("{}", Lorem::from_derive_input(&di).unwrap_err()); +} + +#[test] +fn missing_field() { + let di = parse_quote! { + #[hello(ipsum(amet = true))] + pub struct Foo; + }; + + println!("{}", Lorem::from_derive_input(&di).unwrap_err()); +} diff --git a/third_party/rust/darling/tests/from_generics.rs b/third_party/rust/darling/tests/from_generics.rs new file mode 100644 index 0000000000..5cdd697d6e --- /dev/null +++ b/third_party/rust/darling/tests/from_generics.rs @@ -0,0 +1,175 @@ +//! Tests for `FromGenerics`, and - indirectly - `FromGenericParam`. +//! These tests assume `FromTypeParam` is working and only look at whether the wrappers for magic +//! fields are working as expected. + +use darling::{ + ast::{self, GenericParamExt}, + util::{Ignored, WithOriginal}, + FromDeriveInput, FromTypeParam, Result, +}; + +#[derive(FromDeriveInput)] +#[darling(attributes(lorem))] +struct MyReceiver { + pub generics: ast::Generics<ast::GenericParam<MyTypeParam>>, +} + +#[derive(FromTypeParam)] +#[darling(attributes(lorem))] +struct MyTypeParam { + pub ident: syn::Ident, + #[darling(default)] + pub foo: bool, + pub bar: Option<String>, +} + +fn fdi<T: FromDeriveInput>(src: &str) -> Result<T> { + FromDeriveInput::from_derive_input(&syn::parse_str(src).expect("Source parses")) +} + +/// Verify that `ast::Generics` is populated correctly when there is no generics declaration +#[test] +fn no_generics() { + let rec: MyReceiver = fdi("struct Baz;").expect("Input is well-formed"); + assert!(rec.generics.where_clause.is_none()); + assert_eq!(rec.generics.params.len(), 0); +} + +#[test] +#[allow(clippy::bool_assert_comparison)] +fn expand_some() { + let rec: MyReceiver = fdi(r#" + struct Baz< + 'a, + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(&'a T, U); + "#) + .expect("Input is well-formed"); + assert!(rec.generics.where_clause.is_none()); + + // Make sure we've preserved the lifetime def, though we don't do anything with it. + assert!(rec.generics.params[0].as_lifetime_def().is_some()); + + let mut ty_param_iter = rec.generics.type_params(); + + let first = ty_param_iter + .next() + .expect("type_params should not be empty"); + assert!(first.bar.is_none()); + assert!(first.foo); + assert_eq!(first.ident, "T"); + + let second = ty_param_iter + .next() + .expect("type_params should have a second value"); + assert_eq!( + second + .bar + .as_ref() + .expect("Second type param should set bar"), + "x" + ); + assert_eq!(second.foo, false); + assert_eq!(second.ident, "U"); +} + +/// Verify ≤0.4.1 behavior - where `generics` had to be `syn::Generics` - keeps working. +#[test] +fn passthrough() { + #[derive(FromDeriveInput)] + struct PassthroughReceiver { + pub generics: syn::Generics, + } + + let rec: PassthroughReceiver = fdi(r#" + struct Baz< + 'a, + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(&'a T, U); + "#) + .expect("Input is well-formed"); + + let mut type_param_iter = rec.generics.type_params(); + assert!(type_param_iter.next().is_some()); +} + +/// Verify that `where_clause` is passed through when it exists. +/// As of 0.4.1, there is no `FromWhereClause` trait, so other types aren't supported +/// for that field. +#[test] +fn where_clause() { + let rec: MyReceiver = fdi(r#" + struct Baz< + 'a, + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(&'a T, U) where T: Into<String>; + "#) + .expect("Input is well-formed"); + + assert!(rec.generics.where_clause.is_some()); +} + +/// Test that `WithOriginal` works for generics. +#[test] +fn with_original() { + #[derive(FromDeriveInput)] + struct WorigReceiver { + generics: WithOriginal<ast::Generics<ast::GenericParam<MyTypeParam>>, syn::Generics>, + } + + let rec: WorigReceiver = fdi(r#" + struct Baz< + 'a, + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(&'a T, U) where T: Into<String>; + "#) + .expect("Input is well-formed"); + + // Make sure we haven't lost anything in the conversion + assert_eq!(rec.generics.parsed.params.len(), 3); + assert_eq!(rec.generics.original.params.len(), 3); + + let parsed_t: &MyTypeParam = rec.generics.parsed.params[1] + .as_type_param() + .expect("Second argument should be type param"); + + // Make sure the first type param in each case is T + assert_eq!(parsed_t.ident, "T"); + assert_eq!( + rec.generics + .original + .type_params() + .next() + .expect("First type param should exist") + .ident, + "T" + ); + + // Make sure we actually parsed the first type param + assert!(parsed_t.foo); + assert!(parsed_t.bar.is_none()); +} + +/// Make sure generics can be ignored +#[test] +fn ignored() { + #[derive(FromDeriveInput)] + struct IgnoredReceiver { + generics: Ignored, + } + + let rec: IgnoredReceiver = fdi(r#" + struct Baz< + 'a, + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(&'a T, U) where T: Into<String>; + "#) + .expect("Input is well-formed"); + + assert_eq!(Ignored, rec.generics); +} diff --git a/third_party/rust/darling/tests/from_meta.rs b/third_party/rust/darling/tests/from_meta.rs new file mode 100644 index 0000000000..3e7278cec8 --- /dev/null +++ b/third_party/rust/darling/tests/from_meta.rs @@ -0,0 +1,66 @@ +use darling::{Error, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, FromMeta)] +struct Meta { + #[darling(default)] + meta1: Option<String>, + #[darling(default)] + meta2: bool, +} + +#[test] +fn nested_meta_meta_value() { + let meta = Meta::from_list(&[parse_quote! { + meta1 = "thefeature" + }]) + .unwrap(); + assert_eq!(meta.meta1, Some("thefeature".to_string())); + assert!(!meta.meta2); +} + +#[test] +fn nested_meta_meta_bool() { + let meta = Meta::from_list(&[parse_quote! { + meta2 + }]) + .unwrap(); + assert_eq!(meta.meta1, None); + assert!(meta.meta2); +} + +#[test] +fn nested_meta_lit_string_errors() { + let err = Meta::from_list(&[parse_quote! { + "meta2" + }]) + .unwrap_err(); + assert_eq!( + err.to_string(), + Error::unsupported_format("literal").to_string() + ); +} + +#[test] +fn nested_meta_lit_integer_errors() { + let err = Meta::from_list(&[parse_quote! { + 2 + }]) + .unwrap_err(); + assert_eq!( + err.to_string(), + Error::unsupported_format("literal").to_string() + ); +} + +#[test] +fn nested_meta_lit_bool_errors() { + let err = Meta::from_list(&[parse_quote! { + true + }]) + .unwrap_err(); + assert_eq!( + err.to_string(), + Error::unsupported_format("literal").to_string() + ); +} diff --git a/third_party/rust/darling/tests/from_type_param.rs b/third_party/rust/darling/tests/from_type_param.rs new file mode 100644 index 0000000000..50ec3061e9 --- /dev/null +++ b/third_party/rust/darling/tests/from_type_param.rs @@ -0,0 +1,59 @@ +use darling::FromTypeParam; +use syn::{parse_quote, DeriveInput, GenericParam, Ident, TypeParam}; + +#[derive(FromTypeParam)] +#[darling(attributes(lorem), from_ident)] +struct Lorem { + ident: Ident, + bounds: Vec<syn::TypeParamBound>, + foo: bool, + bar: Option<String>, +} + +impl From<Ident> for Lorem { + fn from(ident: Ident) -> Self { + Lorem { + ident, + foo: false, + bar: None, + bounds: Default::default(), + } + } +} + +fn extract_type(param: &GenericParam) -> &TypeParam { + match *param { + GenericParam::Type(ref ty) => ty, + _ => unreachable!("Not a type param"), + } +} + +#[test] +#[allow(clippy::bool_assert_comparison)] +fn expand_many() { + let di: DeriveInput = parse_quote! { + struct Baz< + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(T, U); + }; + + let params = di.generics.params; + + { + let ty = extract_type(¶ms[0]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.ident, "T"); + assert_eq!(lorem.foo, true); + assert_eq!(lorem.bar, None); + } + + { + let ty = extract_type(¶ms[1]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.ident, "U"); + assert_eq!(lorem.foo, false); + assert_eq!(lorem.bar, Some("x".to_string())); + assert_eq!(lorem.bounds.len(), 2); + } +} diff --git a/third_party/rust/darling/tests/from_type_param_default.rs b/third_party/rust/darling/tests/from_type_param_default.rs new file mode 100644 index 0000000000..9d65665bb9 --- /dev/null +++ b/third_party/rust/darling/tests/from_type_param_default.rs @@ -0,0 +1,53 @@ +use darling::FromTypeParam; +use syn::{parse_quote, DeriveInput, GenericParam, TypeParam}; + +#[derive(Default, FromTypeParam)] +#[darling(attributes(lorem), default)] +struct Lorem { + foo: bool, + bar: Option<String>, + default: Option<syn::Type>, +} + +fn extract_type(param: &GenericParam) -> &TypeParam { + match *param { + GenericParam::Type(ref ty) => ty, + _ => unreachable!("Not a type param"), + } +} + +#[test] +#[allow(clippy::bool_assert_comparison)] +fn expand_many() { + let di: DeriveInput = parse_quote! { + struct Baz< + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized, + #[lorem(foo = false)] V = (), + >(T, U, V); + }; + let params = di.generics.params; + + { + let ty = extract_type(¶ms[0]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.foo, true); + assert_eq!(lorem.bar, None); + } + + { + let ty = extract_type(¶ms[1]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.foo, false); + assert_eq!(lorem.bar, Some("x".to_string())); + assert!(lorem.default.is_none()); + } + + { + let ty = extract_type(¶ms[2]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.foo, false); + assert_eq!(lorem.bar, None); + assert!(lorem.default.is_some()); + } +} diff --git a/third_party/rust/darling/tests/from_variant.rs b/third_party/rust/darling/tests/from_variant.rs new file mode 100644 index 0000000000..e89b8ff7f5 --- /dev/null +++ b/third_party/rust/darling/tests/from_variant.rs @@ -0,0 +1,57 @@ +use darling::FromVariant; +use syn::{spanned::Spanned, Expr, ExprLit, LitInt}; + +#[derive(FromVariant)] +#[darling(from_ident, attributes(hello))] +#[allow(dead_code)] +pub struct Lorem { + ident: syn::Ident, + into: Option<bool>, + skip: Option<bool>, + discriminant: Option<syn::Expr>, + fields: darling::ast::Fields<syn::Type>, +} + +impl From<syn::Ident> for Lorem { + fn from(ident: syn::Ident) -> Self { + Lorem { + ident, + into: Default::default(), + skip: Default::default(), + discriminant: None, + fields: darling::ast::Style::Unit.into(), + } + } +} + +#[test] +fn discriminant() { + let input: syn::DeriveInput = syn::parse_str( + r#" + pub enum Test { + Works = 1, + AlsoWorks = 2, + } + "#, + ) + .unwrap(); + + let span = input.span(); + if let syn::Data::Enum(enm) = input.data { + let lorem = Lorem::from_variant( + enm.variants + .first() + .expect("Hardcoded input has one variant"), + ) + .expect("FromVariant can process the discriminant"); + assert_eq!( + lorem.discriminant, + Some(Expr::Lit(ExprLit { + attrs: vec![], + lit: LitInt::new("1", span).into(), + })) + ) + } else { + panic!("Data should be enum"); + } +} diff --git a/third_party/rust/darling/tests/generics.rs b/third_party/rust/darling/tests/generics.rs new file mode 100644 index 0000000000..ba65781fe3 --- /dev/null +++ b/third_party/rust/darling/tests/generics.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] + +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, Clone, FromMeta)] +struct Wrapper<T>(pub T); + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(hello))] +struct Foo<T> { + lorem: Wrapper<T>, +} + +#[test] +fn expansion() { + let di = parse_quote! { + #[hello(lorem = "Hello")] + pub struct Foo; + }; + + Foo::<String>::from_derive_input(&di).unwrap(); +} diff --git a/third_party/rust/darling/tests/happy_path.rs b/third_party/rust/darling/tests/happy_path.rs new file mode 100644 index 0000000000..a56aee75fc --- /dev/null +++ b/third_party/rust/darling/tests/happy_path.rs @@ -0,0 +1,69 @@ +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Default, FromMeta, PartialEq, Debug)] +#[darling(default)] +struct Lorem { + ipsum: bool, + dolor: Option<String>, +} + +#[derive(FromDeriveInput, PartialEq, Debug)] +#[darling(attributes(darling_demo))] +struct Core { + ident: syn::Ident, + vis: syn::Visibility, + generics: syn::Generics, + lorem: Lorem, +} + +#[derive(FromDeriveInput, PartialEq, Debug)] +#[darling(attributes(darling_demo))] +struct TraitCore { + ident: syn::Ident, + generics: syn::Generics, + lorem: Lorem, +} + +#[test] +fn simple() { + let di = parse_quote! { + #[derive(Foo)] + #[darling_demo(lorem(ipsum))] + pub struct Bar; + }; + + assert_eq!( + Core::from_derive_input(&di).unwrap(), + Core { + ident: parse_quote!(Bar), + vis: parse_quote!(pub), + generics: Default::default(), + lorem: Lorem { + ipsum: true, + dolor: None, + }, + } + ); +} + +#[test] +fn trait_type() { + let di = parse_quote! { + #[derive(Foo)] + #[darling_demo(lorem(dolor = "hello"))] + pub struct Bar; + }; + + assert_eq!( + TraitCore::from_derive_input(&di).unwrap(), + TraitCore { + ident: parse_quote!(Bar), + generics: Default::default(), + lorem: Lorem { + ipsum: false, + dolor: Some("hello".to_owned()), + } + } + ); +} diff --git a/third_party/rust/darling/tests/hash_map.rs b/third_party/rust/darling/tests/hash_map.rs new file mode 100644 index 0000000000..5d9a0114ed --- /dev/null +++ b/third_party/rust/darling/tests/hash_map.rs @@ -0,0 +1,42 @@ +use std::collections::HashMap; + +use darling::FromMeta; +use syn::{parse_quote, Attribute, Path}; + +#[derive(Debug, FromMeta, PartialEq, Eq)] +struct MapValue { + name: String, + #[darling(default)] + option: bool, +} + +#[test] +fn parse_map() { + let attr: Attribute = parse_quote! { + #[foo(first(name = "Hello", option), the::second(name = "Second"))] + }; + + let meta = attr.parse_meta().unwrap(); + let map: HashMap<Path, MapValue> = FromMeta::from_meta(&meta).unwrap(); + + let comparison: HashMap<Path, MapValue> = vec![ + ( + parse_quote!(first), + MapValue { + name: "Hello".into(), + option: true, + }, + ), + ( + parse_quote!(the::second), + MapValue { + name: "Second".into(), + option: false, + }, + ), + ] + .into_iter() + .collect(); + + assert_eq!(comparison, map); +} diff --git a/third_party/rust/darling/tests/multiple.rs b/third_party/rust/darling/tests/multiple.rs new file mode 100644 index 0000000000..b2243e62b6 --- /dev/null +++ b/third_party/rust/darling/tests/multiple.rs @@ -0,0 +1,30 @@ +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(FromDeriveInput)] +#[darling(attributes(hello))] +#[allow(dead_code)] +struct Lorem { + ident: syn::Ident, + ipsum: Ipsum, +} + +#[derive(FromMeta)] +struct Ipsum { + #[darling(multiple)] + dolor: Vec<String>, +} + +#[test] +fn expand_many() { + let di = parse_quote! { + #[hello(ipsum(dolor = "Hello", dolor = "World"))] + pub struct Baz; + }; + + let lorem: Lorem = Lorem::from_derive_input(&di).unwrap(); + assert_eq!( + lorem.ipsum.dolor, + vec!["Hello".to_string(), "World".to_string()] + ); +} diff --git a/third_party/rust/darling/tests/newtype.rs b/third_party/rust/darling/tests/newtype.rs new file mode 100644 index 0000000000..10d0238882 --- /dev/null +++ b/third_party/rust/darling/tests/newtype.rs @@ -0,0 +1,26 @@ +//! A newtype struct should be able to derive `FromMeta` if its member implements it. + +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, FromMeta, PartialEq, Eq)] +struct Lorem(bool); + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(newtype))] +struct DemoContainer { + lorem: Lorem, +} + +#[test] +fn generated() { + let di = parse_quote! { + #[derive(Baz)] + #[newtype(lorem = false)] + pub struct Foo; + }; + + let c = DemoContainer::from_derive_input(&di).unwrap(); + + assert_eq!(c.lorem, Lorem(false)); +} diff --git a/third_party/rust/darling/tests/skip.rs b/third_party/rust/darling/tests/skip.rs new file mode 100644 index 0000000000..f930ca5d31 --- /dev/null +++ b/third_party/rust/darling/tests/skip.rs @@ -0,0 +1,74 @@ +//! Test that skipped fields are not read into structs when they appear in input. + +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, PartialEq, Eq, FromDeriveInput)] +#[darling(attributes(skip_test))] +pub struct Lorem { + ipsum: String, + + #[darling(skip)] + dolor: u8, +} + +/// Verify variant-level and field-level skip work correctly for enums. +#[derive(Debug, FromMeta)] +pub enum Sit { + Amet(bool), + + #[darling(skip)] + Foo { + hello: bool, + }, + + Bar { + hello: bool, + #[darling(skip)] + world: u8, + }, +} + +#[test] +fn verify_skipped_field_not_required() { + let di = parse_quote! { + #[skip_test(ipsum = "Hello")] + struct Baz; + }; + + assert_eq!( + Lorem::from_derive_input(&di).unwrap(), + Lorem { + ipsum: "Hello".to_string(), + dolor: 0, + } + ); +} + +/// This test verifies that a skipped field will still prefer an explicit default +/// over the default that would come from its field type. It would be incorrect for +/// `Defaulting::from_derive_input` to fail here, and it would be wrong for the value +/// of `dolor` to be `None`. +#[test] +fn verify_default_supersedes_from_none() { + fn default_dolor() -> Option<u8> { + Some(2) + } + + #[derive(Debug, PartialEq, Eq, FromDeriveInput)] + #[darling(attributes(skip_test))] + pub struct Defaulting { + #[darling(skip, default = "default_dolor")] + dolor: Option<u8>, + } + + let di = parse_quote! { + #[skip_test] + struct Baz; + }; + + assert_eq!( + Defaulting::from_derive_input(&di).unwrap(), + Defaulting { dolor: Some(2) } + ) +} diff --git a/third_party/rust/darling/tests/split_declaration.rs b/third_party/rust/darling/tests/split_declaration.rs new file mode 100644 index 0000000000..8db11d0f03 --- /dev/null +++ b/third_party/rust/darling/tests/split_declaration.rs @@ -0,0 +1,67 @@ +//! When input is split across multiple attributes on one element, +//! darling should collapse that into one struct. + +use darling::{Error, FromDeriveInput}; +use syn::parse_quote; + +#[derive(Debug, FromDeriveInput, PartialEq, Eq)] +#[darling(attributes(split))] +struct Lorem { + foo: String, + bar: bool, +} + +#[test] +fn split_attributes_accrue_to_instance() { + let di = parse_quote! { + #[split(foo = "Hello")] + #[split(bar)] + pub struct Foo; + }; + + let parsed = Lorem::from_derive_input(&di).unwrap(); + assert_eq!( + parsed, + Lorem { + foo: "Hello".to_string(), + bar: true, + } + ); +} + +#[test] +fn duplicates_across_split_attrs_error() { + let di = parse_quote! { + #[split(foo = "Hello")] + #[split(foo = "World", bar)] + pub struct Foo; + }; + + let pr = Lorem::from_derive_input(&di).unwrap_err(); + assert!(pr.has_span()); + assert_eq!(pr.to_string(), Error::duplicate_field("foo").to_string()); +} + +#[test] +fn multiple_errors_accrue_to_instance() { + let di = parse_quote! { + #[split(foo = "Hello")] + #[split(foo = "World")] + pub struct Foo; + }; + + let pr = Lorem::from_derive_input(&di); + let err: Error = pr.unwrap_err(); + assert_eq!(2, err.len()); + let mut errs = err.into_iter().peekable(); + assert_eq!( + errs.peek().unwrap().to_string(), + Error::duplicate_field("foo").to_string() + ); + assert!(errs.next().unwrap().has_span()); + assert_eq!( + errs.next().unwrap().to_string(), + Error::missing_field("bar").to_string() + ); + assert!(errs.next().is_none()); +} diff --git a/third_party/rust/darling/tests/suggestions.rs b/third_party/rust/darling/tests/suggestions.rs new file mode 100644 index 0000000000..5baf76ca6c --- /dev/null +++ b/third_party/rust/darling/tests/suggestions.rs @@ -0,0 +1,29 @@ +#![allow(dead_code)] +#![cfg(feature = "suggestions")] + +use darling::{FromDeriveInput, FromMeta}; +use syn::parse_quote; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(suggest))] +struct Lorem { + ipsum: String, + dolor: Dolor, +} + +#[derive(Debug, FromMeta)] +struct Dolor { + sit: bool, +} + +#[test] +fn suggest_dolor() { + let input: syn::DeriveInput = parse_quote! { + #[suggest(ipsum = "Hello", dolorr(sit))] + pub struct Foo; + }; + + let result = Lorem::from_derive_input(&input).unwrap_err(); + assert_eq!(2, result.len()); + assert!(format!("{}", result).contains("Did you mean")); +} diff --git a/third_party/rust/darling/tests/supports.rs b/third_party/rust/darling/tests/supports.rs new file mode 100644 index 0000000000..d6c7556722 --- /dev/null +++ b/third_party/rust/darling/tests/supports.rs @@ -0,0 +1,90 @@ +use darling::{ast, FromDeriveInput, FromVariant}; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(from_variants), supports(enum_any))] +pub struct Container { + // The second type parameter can be anything that implements FromField, since + // FromDeriveInput will produce an error if given a struct. + data: ast::Data<Variant, ()>, +} + +#[derive(Default, Debug, FromVariant)] +#[darling(default, attributes(from_variants), supports(newtype, unit))] +pub struct Variant { + into: Option<bool>, + skip: Option<bool>, +} + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(from_struct), supports(struct_named))] +pub struct StructContainer { + // The second type parameter can be anything that implements FromVariant, since + // FromDeriveInput will produce an error if given an enum. + data: ast::Data<(), syn::Field>, +} + +mod source { + use syn::{parse_quote, DeriveInput}; + + pub fn newtype_enum() -> DeriveInput { + parse_quote! { + enum Hello { + World(bool), + String(String), + } + } + } + + pub fn named_field_enum() -> DeriveInput { + parse_quote! { + enum Hello { + Foo(u16), + World { + name: String + }, + } + } + } + + pub fn empty_enum() -> DeriveInput { + parse_quote! { + enum Hello {} + } + } + + pub fn named_struct() -> DeriveInput { + parse_quote! { + struct Hello { + world: bool, + } + } + } + + pub fn tuple_struct() -> DeriveInput { + parse_quote! { struct Hello(String, bool); } + } +} + +#[test] +fn enum_newtype_or_unit() { + // Should pass + let container = Container::from_derive_input(&source::newtype_enum()).unwrap(); + assert!(container.data.is_enum()); + + // Should error + Container::from_derive_input(&source::named_field_enum()).unwrap_err(); + Container::from_derive_input(&source::named_struct()).unwrap_err(); +} + +#[test] +fn struct_named() { + // Should pass + let container = StructContainer::from_derive_input(&source::named_struct()).unwrap(); + assert!(container.data.is_struct()); + + // Should fail + StructContainer::from_derive_input(&source::tuple_struct()).unwrap_err(); + StructContainer::from_derive_input(&source::named_field_enum()).unwrap_err(); + StructContainer::from_derive_input(&source::newtype_enum()).unwrap_err(); + StructContainer::from_derive_input(&source::empty_enum()).unwrap_err(); +} diff --git a/third_party/rust/darling/tests/unsupported_attributes.rs b/third_party/rust/darling/tests/unsupported_attributes.rs new file mode 100644 index 0000000000..14edf2c858 --- /dev/null +++ b/third_party/rust/darling/tests/unsupported_attributes.rs @@ -0,0 +1,49 @@ +use darling::FromDeriveInput; +use syn::{parse_quote, Ident, LitStr, Path}; + +#[derive(Debug, FromDeriveInput)] +#[darling(supports(struct_unit), attributes(bar))] +pub struct Bar { + pub ident: Ident, + pub st: Path, + pub file: LitStr, +} + +/// Per [#96](https://github.com/TedDriggs/darling/issues/96), make sure that an +/// attribute which isn't a valid meta gets an error. +#[test] +fn non_meta_attribute_gets_own_error() { + let di = parse_quote! { + #[derive(Bar)] + #[bar(file = "motors/example_6.csv", st = RocketEngine)] + pub struct EstesC6; + }; + + let errors: darling::Error = Bar::from_derive_input(&di).unwrap_err().flatten(); + // The number of errors here is 1 for the bad attribute + 2 for the missing fields + assert_eq!(3, errors.len()); + // Make sure one of the errors propagates the syn error + assert!(errors + .into_iter() + .any(|e| e.to_string().contains("expected lit"))); +} + +/// Properties can be split across multiple attributes; this test ensures that one +/// non-meta attribute does not interfere with the parsing of other, well-formed attributes. +#[test] +fn non_meta_attribute_does_not_block_others() { + let di = parse_quote! { + #[derive(Bar)] + #[bar(st = RocketEngine)] + #[bar(file = "motors/example_6.csv")] + pub struct EstesC6; + }; + + let errors: darling::Error = Bar::from_derive_input(&di).unwrap_err().flatten(); + // The number of errors here is 1 for the bad attribute + 1 for the missing "st" field + assert_eq!(2, errors.len()); + // Make sure one of the errors propagates the syn error + assert!(errors + .into_iter() + .any(|e| e.to_string().contains("expected lit"))); +} |