summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Geisler <mgeisler@google.com>2024-03-21 15:42:53 +0100
committerMartin Geisler <mgeisler@google.com>2024-03-26 17:46:47 +0100
commit8ddb0abb828ad2993c3a3cd23e0f24aa6f9f736f (patch)
treedef60f9ddc8691c7d7e8d772082afa058d6272bf
parentf9a69249116bbd1ab777f11e807c812e294ce83d (diff)
downloaddarling-8ddb0abb828ad2993c3a3cd23e0f24aa6f9f736f.tar.gz
Import 'darling' crateupstream
Request Document: go/android-rust-importing-crates For CL Reviewers: go/android3p#cl-review For Build Team: go/ab-third-party-imports Bug: http://b/328420253 Test: m libdarling Change-Id: I759dddf9d1306135cef49357590d7853589418ba
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--.gitignore9
-rw-r--r--Android.bp609
-rw-r--r--CHANGELOG.md303
-rw-r--r--Cargo.toml58
-rw-r--r--LICENSE21
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS2
-rw-r--r--README.md151
-rw-r--r--cargo_embargo.json10
-rw-r--r--clippy.toml2
-rwxr-xr-xcompiletests.sh1
-rw-r--r--examples/automatic_bounds.rs73
-rw-r--r--examples/consume_fields.rs175
-rw-r--r--examples/expr_with.rs19
-rw-r--r--examples/fallible_read.rs85
-rw-r--r--examples/heterogeneous_enum_and_word.rs79
-rw-r--r--examples/shorthand_or_long_field.rs79
-rw-r--r--examples/supports_struct.rs61
-rw-r--r--src/lib.rs110
-rw-r--r--src/macros_public.rs96
-rw-r--r--tests/accrue_errors.rs102
-rw-r--r--tests/attrs_with.rs80
-rw-r--r--tests/compile-fail/attrs_with_bad_fn.rs15
-rw-r--r--tests/compile-fail/attrs_with_bad_fn.stderr20
-rw-r--r--tests/compile-fail/default_expr_wrong_type.rs12
-rw-r--r--tests/compile-fail/default_expr_wrong_type.stderr21
-rw-r--r--tests/compile-fail/duplicate_word_across_variants.rs12
-rw-r--r--tests/compile-fail/duplicate_word_across_variants.stderr11
-rw-r--r--tests/compile-fail/duplicate_word_on_variant.rs10
-rw-r--r--tests/compile-fail/duplicate_word_on_variant.stderr5
-rw-r--r--tests/compile-fail/flatten_meta_conflicts.rs21
-rw-r--r--tests/compile-fail/flatten_meta_conflicts.stderr17
-rw-r--r--tests/compile-fail/flatten_multiple_fields.rs28
-rw-r--r--tests/compile-fail/flatten_multiple_fields.stderr23
-rw-r--r--tests/compile-fail/not_impl_from_meta.rs16
-rw-r--r--tests/compile-fail/not_impl_from_meta.stderr33
-rw-r--r--tests/compile-fail/skip_field_not_impl_default.rs18
-rw-r--r--tests/compile-fail/skip_field_not_impl_default.stderr21
-rw-r--r--tests/compile-fail/word_on_wrong_variant_type.rs10
-rw-r--r--tests/compile-fail/word_on_wrong_variant_type.stderr5
-rw-r--r--tests/compiletests.rs16
-rw-r--r--tests/computed_bound.rs42
-rw-r--r--tests/custom_bound.rs25
-rw-r--r--tests/defaults.rs189
-rw-r--r--tests/enums_default.rs44
-rw-r--r--tests/enums_newtype.rs90
-rw-r--r--tests/enums_struct.rs15
-rw-r--r--tests/enums_unit.rs14
-rw-r--r--tests/error.rs54
-rw-r--r--tests/flatten.rs181
-rw-r--r--tests/flatten_error_accumulation.rs45
-rw-r--r--tests/flatten_from_field.rs84
-rw-r--r--tests/from_generics.rs175
-rw-r--r--tests/from_meta.rs109
-rw-r--r--tests/from_type_param.rs59
-rw-r--r--tests/from_type_param_default.rs53
-rw-r--r--tests/from_variant.rs57
-rw-r--r--tests/generics.rs23
-rw-r--r--tests/happy_path.rs69
-rw-r--r--tests/hash_map.rs42
-rw-r--r--tests/multiple.rs30
-rw-r--r--tests/newtype.rs26
-rw-r--r--tests/skip.rs74
-rw-r--r--tests/split_declaration.rs67
-rw-r--r--tests/suggestions.rs44
-rw-r--r--tests/supports.rs90
-rw-r--r--tests/unsupported_attributes.rs31
69 files changed, 4197 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..df21d32
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "25ce6ecc079c58a18cbecbdcc98bf8d647410edd"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d83adf0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+/target
+**/target
+**/*.rs.bk
+Cargo.lock
+.vscode
+.rls.toml
+.DS_STORE
+[._]*.sw?
+[._]sw?
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..0c9efe3
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,609 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file as changes will be overridden on upgrade.
+
+// TODO: Add license.
+rust_test_host {
+ name: "darling_test_tests_accrue_errors",
+ crate_name: "accrue_errors",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/accrue_errors.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_attrs_with",
+ crate_name: "attrs_with",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/attrs_with.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_computed_bound",
+ crate_name: "computed_bound",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/computed_bound.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_custom_bound",
+ crate_name: "custom_bound",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/custom_bound.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_defaults",
+ crate_name: "defaults",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/defaults.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_enums_default",
+ crate_name: "enums_default",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/enums_default.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_enums_newtype",
+ crate_name: "enums_newtype",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/enums_newtype.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_enums_struct",
+ crate_name: "enums_struct",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/enums_struct.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_enums_unit",
+ crate_name: "enums_unit",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/enums_unit.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_error",
+ crate_name: "error",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/error.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_flatten",
+ crate_name: "flatten",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/flatten.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_flatten_error_accumulation",
+ crate_name: "flatten_error_accumulation",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/flatten_error_accumulation.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_flatten_from_field",
+ crate_name: "flatten_from_field",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/flatten_from_field.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_from_generics",
+ crate_name: "from_generics",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/from_generics.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_from_meta",
+ crate_name: "from_meta",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/from_meta.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_from_type_param",
+ crate_name: "from_type_param",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/from_type_param.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_from_type_param_default",
+ crate_name: "from_type_param_default",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/from_type_param_default.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_from_variant",
+ crate_name: "from_variant",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/from_variant.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_generics",
+ crate_name: "generics",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/generics.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_happy_path",
+ crate_name: "happy_path",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/happy_path.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_hash_map",
+ crate_name: "hash_map",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/hash_map.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_multiple",
+ crate_name: "multiple",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/multiple.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_newtype",
+ crate_name: "newtype",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/newtype.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_skip",
+ crate_name: "skip",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/skip.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_split_declaration",
+ crate_name: "split_declaration",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/split_declaration.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_supports",
+ crate_name: "supports",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/supports.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_test_host {
+ name: "darling_test_tests_unsupported_attributes",
+ crate_name: "unsupported_attributes",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["tests/unsupported_attributes.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libdarling",
+ "libdarling_core",
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ proc_macros: ["libdarling_macro"],
+}
+
+rust_library_host {
+ name: "libdarling",
+ crate_name: "darling",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.20.8",
+ srcs: ["src/lib.rs"],
+ edition: "2018",
+ rustlibs: ["libdarling_core"],
+ proc_macros: ["libdarling_macro"],
+ compile_multilib: "first",
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..931a8c0
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,303 @@
+# Changelog
+
+## v0.20.8 (February 23, 2024)
+
+- Add `#[darling(with = ...)]` support to `attrs` magic field to allow using custom receiver types for `attrs` [#273](https://github.com/TedDriggs/darling/issues/273)
+
+## v0.20.7 (February 22, 2024)
+
+- Add `#[darling(flatten)]` to allow forwarding unknown fields to another struct [#146](https://github.com/TedDriggs/darling/issues/146)
+- Don't suggest names of skipped fields in derived impls [#268](https://github.com/TedDriggs/darling/issues/268)
+
+## v0.20.6 (February 14, 2024)
+
+- Fix some missing syn invisible group handling in `FromMeta` impls [#263](https://github.com/TedDriggs/darling/pull/263)
+- Fix misleading error message on `Error::unexpected_type` so it no longer implies the type was a literal [#264](https://github.com/TedDriggs/darling/pull/264)
+- Impl `FromMeta` `Vec` of literals, e.g. `LitStr` [#265](https://github.com/TedDriggs/pull/265)
+
+## v0.20.5 (January 30, 2024)
+
+- Add `Flag::span` inherent method, as `Flag` can no longer impl `syn::spanned::Spanned` [#242](https://github.com/TedDriggs/darling/issues/242)
+
+## v0.20.4 (January 29, 2024)
+
+- Accept bare paths in `#[darling(default = ...)]` [#258](https://github.com/TedDriggs/darling/pull/258)
+- Add `FromMeta` impl for `PathBuf` [#259](https://github.com/TedDriggs/darling/pull/259)
+- Improve `FromMeta` implementation for enums [#260](https://github.com/TedDriggs/darling/pull/260)
+ - Properly implement unit arms in `FromMeta::from_list` to provide a consistent API for heterogeneous enums that include a mix of unit, newtype and struct variants
+ - Add `#[darling(word)]` attribute for unit enum variants (See [#63](https://github.com/TedDriggs/darling/issues/63) for details)
+
+## v0.20.3 (July 12, 2023)
+
+- Add `FromMeta` impl for `u128` and `i128` [#243](https://github.com/TedDriggs/darling/pull/243)
+
+## v0.20.2 (May 25, 2023)
+
+- Allow darling users to omit quotation marks for paths and idents [#236](https://github.com/TedDriggs/darling/pull/236)
+- Add new util functions for controlling how quotation marks are handled when reading into `Expr` fields [#235](https://github.com/TedDriggs/darling/pull/235)
+
+## v0.20.1 (May 2, 2023)
+
+- Add `Clone` impl for `NestedMeta` [#230](https://github.com/TedDriggs/darling/pull/230)
+
+## v0.20.0 (April 27, 2023)
+
+- Bump syn to version 2, courtesy of @jonasbb [#227](https://github.com/TedDriggs/darling/issues/227)
+
+### Breaking Changes
+
+- Replace all occurrences of syn::NestedMeta with darling::ast::NestedMeta.
+
+- Replacement for the deprecated AttributeArgs:
+
+```rust
+// Before
+
+parse_macro_input!(args as AttributeArgs);
+
+// After
+
+match NestedMeta::parse_meta_list(args) {
+ Ok(v) => v,
+ Err(e) => {
+ return TokenStream::from(Error::from(e).write_errors());
+ }
+};
+```
+
+- In GenericParamExt, `LifetimeDef` is now `LifetimeParam`.
+- In GenericParamExt, `as_lifetime_def` is renamed to `as_lifetime_param`.
+- Flag and SpannedValue no longer implement `syn::spanned::Spanned`.
+- The MSRV (minimum supported Rust version) is now 1.56, because of syn.
+
+### Deprecation Warnings
+
+In previous versions of `darling`, arbitrary expressions were passed in attributes by wrapping them in quotation marks.
+v0.20.0 preserves this behavior for `syn::Expr`, but as a result a field expecting a `syn::Expr` cannot accept a string literal - it will incorrectly attempt to parse the contents. If this is an issue for you, please add a comment to [#229](https://github.com/TedDriggs/darling/issues/229).
+
+## v0.14.4 (March 9, 2023)
+
+- Add support for child diagnostics when `diagnostics` feature enabled [#224](https://github.com/TedDriggs/darling/issues/224)
+
+## 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/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..61a0c60
--- /dev/null
+++ b/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.20.8"
+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.20.8"
+readme = "README.md"
+license = "MIT"
+repository = "https://github.com/TedDriggs/darling"
+
+[dependencies.darling_core]
+version = "=0.20.8"
+
+[dependencies.darling_macro]
+version = "=0.20.8"
+
+[dev-dependencies.proc-macro2]
+version = "1.0.37"
+
+[dev-dependencies.quote]
+version = "1.0.18"
+
+[dev-dependencies.syn]
+version = "2.0.15"
+
+[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/LICENSE b/LICENSE
new file mode 100644
index 0000000..0b48ead
--- /dev/null
+++ b/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/METADATA b/METADATA
new file mode 100644
index 0000000..74f306c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "darling"
+description: "()"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "darling"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/darling/darling-0.20.8.crate"
+ primary_source: true
+ }
+ version: "0.20.8"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2024
+ month: 3
+ day: 6
+ }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..48bea6e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..023e8ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,151 @@
+# 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.56+](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)]
+
+`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::{Error, FromMeta};
+use darling::ast::NestedMeta;
+use syn::ItemFn;
+use proc_macro::TokenStream;
+
+#[derive(Debug, FromMeta)]
+struct MacroArgs {
+ #[darling(default)]
+ timeout_ms: Option<u16>,
+ path: String,
+}
+
+#[proc_macro_attribute]
+pub fn your_attr(args: TokenStream, input: TokenStream) -> TokenStream {
+ let attr_args = match NestedMeta::parse_meta_list(args.into()) {
+ Ok(v) => v,
+ Err(e) => { return TokenStream::from(Error::from(e).write_errors()); }
+ };
+ let _input = syn::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`.
+ - **Transformation of forwarded attributes**: You can add `#[darling(with=path)]` to the `attrs` field to use a custom function to transform the forwarded attributes before they're provided to your struct. The function signature is `fn(Vec<Attribute>) -> darling::Result<T>`, where `T` is the type you declared for the `attrs` field. Returning an error from this function will propagate with all other parsing errors.
+- **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.
+- **Struct flattening**: Use `#[darling(flatten)]` to remove one level of structure when presenting your meta item to users. Fields that are not known to the parent struct will be forwarded to the `flatten` field.
+
+## 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.
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..ef024dc
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,10 @@
+{
+ "tests": true,
+ "features": [],
+ "package": {
+ "darling": {
+ "device_supported": false,
+ "host_first_multilib": true
+ }
+ }
+}
diff --git a/clippy.toml b/clippy.toml
new file mode 100644
index 0000000..e221e79
--- /dev/null
+++ b/clippy.toml
@@ -0,0 +1,2 @@
+msrv = "1.56.0"
+disallowed-names = [] # we want to be able to use placeholder names in tests \ No newline at end of file
diff --git a/compiletests.sh b/compiletests.sh
new file mode 100755
index 0000000..a8ce042
--- /dev/null
+++ b/compiletests.sh
@@ -0,0 +1 @@
+RUSTFLAGS="--cfg=compiletests" cargo +1.65.0 test --test compiletests \ No newline at end of file
diff --git a/examples/automatic_bounds.rs b/examples/automatic_bounds.rs
new file mode 100644
index 0000000..8312afe
--- /dev/null
+++ b/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/examples/consume_fields.rs b/examples/consume_fields.rs
new file mode 100644
index 0000000..f5cd435
--- /dev/null
+++ b/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/examples/expr_with.rs b/examples/expr_with.rs
new file mode 100644
index 0000000..037ce25
--- /dev/null
+++ b/examples/expr_with.rs
@@ -0,0 +1,19 @@
+use darling::{util::parse_expr, FromDeriveInput};
+use syn::{parse_quote, Expr};
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(demo))]
+pub struct Receiver {
+ #[darling(with = parse_expr::preserve_str_literal, map = Some)]
+ example1: Option<Expr>,
+}
+
+fn main() {
+ let input = Receiver::from_derive_input(&parse_quote! {
+ #[demo(example1 = test::path)]
+ struct Example;
+ })
+ .unwrap();
+
+ assert_eq!(input.example1, Some(parse_quote!(test::path)));
+}
diff --git a/examples/fallible_read.rs b/examples/fallible_read.rs
new file mode 100644
index 0000000..848c3d2
--- /dev/null
+++ b/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)?).unsigned_abs(),
+ };
+
+ 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/examples/heterogeneous_enum_and_word.rs b/examples/heterogeneous_enum_and_word.rs
new file mode 100644
index 0000000..029549f
--- /dev/null
+++ b/examples/heterogeneous_enum_and_word.rs
@@ -0,0 +1,79 @@
+//! This example demonstrates:
+//!
+//! - The behavior of a derived `FromMeta` implementation for heterogeneous enums
+//! (i.e. enums that include a mix of unit, newtype and struct variants).
+//! - Using `#[darling(word)]` to specify a unit variant to use when a receiver field
+//! is specified without a value (i.e. a unit variant to use for deriving the
+//! `FromMeta::from_word` method).
+//! - Using `#[darling(default)]` on a receiver field to fall back to `Default::default()`
+//! for the enum's value when the receiver field is not specified by the caller.
+
+use darling::{Error, FromDeriveInput, FromMeta};
+use syn::parse_quote;
+
+/// A playback volume.
+#[derive(Debug, FromMeta, PartialEq, Eq)]
+enum Volume {
+ Normal,
+ #[darling(word)]
+ Low,
+ High,
+ #[darling(rename = "dB")]
+ Decibels(u8),
+}
+
+impl Default for Volume {
+ fn default() -> Self {
+ Volume::Normal
+ }
+}
+
+#[derive(Debug, FromDeriveInput)]
+#[darling(attributes(play))]
+struct PlayReceiver {
+ #[darling(default)]
+ volume: Volume,
+}
+
+fn main() {
+ // `Default::default()` is used when `volume` is not specified.
+ let missing_volume = PlayReceiver::from_derive_input(&parse_quote! {
+ #[play]
+ struct Player;
+ })
+ .unwrap();
+ assert_eq!(Volume::Normal, missing_volume.volume);
+
+ // `#[darling(word)]` unit variant is used when `volume` is specified as a word with no value.
+ let empty_volume = PlayReceiver::from_derive_input(&parse_quote! {
+ #[play(volume)]
+ struct Player;
+ })
+ .unwrap();
+ assert_eq!(Volume::Low, empty_volume.volume);
+
+ // Specified `volume` value is used when provided.
+ let unit_variant_volume = PlayReceiver::from_derive_input(&parse_quote! {
+ #[play(volume(high))]
+ struct Player;
+ })
+ .unwrap();
+ assert_eq!(Volume::High, unit_variant_volume.volume);
+ let newtype_volume = PlayReceiver::from_derive_input(&parse_quote! {
+ #[play(volume(dB = 100))]
+ struct Player;
+ })
+ .unwrap();
+ assert_eq!(Volume::Decibels(100), newtype_volume.volume);
+
+ // Multiple `volume` values result in an error.
+ let err = PlayReceiver::from_derive_input(&parse_quote! {
+ #[play(volume(low, dB = 20))]
+ struct Player;
+ })
+ .unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ Error::too_many_items(1).at("volume").to_string()
+ );
+}
diff --git a/examples/shorthand_or_long_field.rs b/examples/shorthand_or_long_field.rs
new file mode 100644
index 0000000..750d83e
--- /dev/null
+++ b/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/examples/supports_struct.rs b/examples/supports_struct.rs
new file mode 100644
index 0000000..97a5bb9
--- /dev/null
+++ b/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/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..657a6ff
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,110 @@
+//! # 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;
+
+ pub use crate::ast::NestedMeta;
+}
+
+#[macro_use]
+mod macros_public;
diff --git a/src/macros_public.rs b/src/macros_public.rs
new file mode 100644
index 0000000..c264fcc
--- /dev/null
+++ b/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/tests/accrue_errors.rs b/tests/accrue_errors.rs
new file mode 100644
index 0000000..e8799c4
--- /dev/null
+++ b/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/tests/attrs_with.rs b/tests/attrs_with.rs
new file mode 100644
index 0000000..198322d
--- /dev/null
+++ b/tests/attrs_with.rs
@@ -0,0 +1,80 @@
+use std::collections::BTreeSet;
+
+use darling::{util, Error, FromDeriveInput, Result};
+use syn::{parse_quote, Attribute};
+
+fn unique_idents(attrs: Vec<Attribute>) -> Result<BTreeSet<String>> {
+ let mut errors = Error::accumulator();
+ let idents = attrs
+ .into_iter()
+ .filter_map(|attr| {
+ let path = attr.path();
+ errors.handle(
+ path.get_ident()
+ .map(std::string::ToString::to_string)
+ .ok_or_else(|| {
+ Error::custom(format!("`{}` is not an ident", util::path_to_string(path)))
+ .with_span(path)
+ }),
+ )
+ })
+ .collect();
+
+ errors.finish_with(idents)
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(a), forward_attrs)]
+struct Receiver {
+ #[darling(with = unique_idents)]
+ attrs: BTreeSet<String>,
+ other: Option<bool>,
+}
+
+#[test]
+fn succeeds_on_no_attrs() {
+ let di = Receiver::from_derive_input(&parse_quote! {
+ struct Demo;
+ })
+ .unwrap();
+
+ assert!(di.attrs.is_empty());
+}
+
+#[test]
+fn succeeds_on_valid_input() {
+ let di = Receiver::from_derive_input(&parse_quote! {
+ #[allow(dead_code)]
+ /// testing
+ #[another]
+ struct Demo;
+ })
+ .unwrap();
+
+ assert_eq!(di.attrs.len(), 3);
+ assert!(di.attrs.contains("allow"));
+ assert!(di.attrs.contains("another"));
+ assert!(di.attrs.contains("doc"));
+ assert_eq!(di.other, None);
+}
+
+#[test]
+fn errors_combined_with_others() {
+ let e = Receiver::from_derive_input(&parse_quote! {
+ #[path::to::attr(dead_code)]
+ #[a(other = 5)]
+ struct Demo;
+ })
+ .map(|_| "Should have failed")
+ .unwrap_err();
+
+ let error = e.to_string();
+
+ assert_eq!(e.len(), 2);
+
+ // Look for the error on the field `other`
+ assert!(error.contains("at other"));
+
+ // Look for the invalid path from attrs conversion
+ assert!(error.contains("`path::to::attr`"));
+}
diff --git a/tests/compile-fail/attrs_with_bad_fn.rs b/tests/compile-fail/attrs_with_bad_fn.rs
new file mode 100644
index 0000000..6063f64
--- /dev/null
+++ b/tests/compile-fail/attrs_with_bad_fn.rs
@@ -0,0 +1,15 @@
+use darling::FromDeriveInput;
+use syn::Attribute;
+
+fn bad_converter(attrs: Vec<Attribute>) -> Vec<Attribute> {
+ attrs
+}
+
+#[derive(FromDeriveInput)]
+#[darling(forward_attrs)]
+struct Receiver {
+ #[darling(with = bad_converter)]
+ attrs: Vec<Attribute>,
+}
+
+fn main() {}
diff --git a/tests/compile-fail/attrs_with_bad_fn.stderr b/tests/compile-fail/attrs_with_bad_fn.stderr
new file mode 100644
index 0000000..efdc201
--- /dev/null
+++ b/tests/compile-fail/attrs_with_bad_fn.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+ --> tests/compile-fail/attrs_with_bad_fn.rs:11:22
+ |
+11 | #[darling(with = bad_converter)]
+ | ^^^^^^^^^^^^^
+ | |
+ | expected enum `Result`, found struct `Vec`
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Result<_, darling::Error>`
+ found struct `Vec<Attribute>`
+note: associated function defined here
+ --> core/src/error/mod.rs
+ |
+ | pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
+ | ^^^^^^
+help: try wrapping the expression in `Ok`
+ |
+11 | #[darling(with = Ok(bad_converter))]
+ | +++ +
diff --git a/tests/compile-fail/default_expr_wrong_type.rs b/tests/compile-fail/default_expr_wrong_type.rs
new file mode 100644
index 0000000..cd9a0fd
--- /dev/null
+++ b/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/tests/compile-fail/default_expr_wrong_type.stderr b/tests/compile-fail/default_expr_wrong_type.stderr
new file mode 100644
index 0000000..a8b7c50
--- /dev/null
+++ b/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/tests/compile-fail/duplicate_word_across_variants.rs b/tests/compile-fail/duplicate_word_across_variants.rs
new file mode 100644
index 0000000..3c8a8f8
--- /dev/null
+++ b/tests/compile-fail/duplicate_word_across_variants.rs
@@ -0,0 +1,12 @@
+use darling::FromMeta;
+
+#[derive(FromMeta)]
+enum Choice {
+ #[darling(word)]
+ A,
+ #[darling(word)]
+ B,
+ C,
+}
+
+fn main() {}
diff --git a/tests/compile-fail/duplicate_word_across_variants.stderr b/tests/compile-fail/duplicate_word_across_variants.stderr
new file mode 100644
index 0000000..6f8c4bb
--- /dev/null
+++ b/tests/compile-fail/duplicate_word_across_variants.stderr
@@ -0,0 +1,11 @@
+error: `#[darling(word)]` can only be applied to one variant
+ --> tests/compile-fail/duplicate_word_across_variants.rs:5:15
+ |
+5 | #[darling(word)]
+ | ^^^^
+
+error: `#[darling(word)]` can only be applied to one variant
+ --> tests/compile-fail/duplicate_word_across_variants.rs:7:15
+ |
+7 | #[darling(word)]
+ | ^^^^
diff --git a/tests/compile-fail/duplicate_word_on_variant.rs b/tests/compile-fail/duplicate_word_on_variant.rs
new file mode 100644
index 0000000..e294292
--- /dev/null
+++ b/tests/compile-fail/duplicate_word_on_variant.rs
@@ -0,0 +1,10 @@
+use darling::FromMeta;
+
+#[derive(FromMeta)]
+enum Choice {
+ #[darling(word, word)]
+ A,
+ B,
+}
+
+fn main() {}
diff --git a/tests/compile-fail/duplicate_word_on_variant.stderr b/tests/compile-fail/duplicate_word_on_variant.stderr
new file mode 100644
index 0000000..bc489d4
--- /dev/null
+++ b/tests/compile-fail/duplicate_word_on_variant.stderr
@@ -0,0 +1,5 @@
+error: Duplicate field `word`
+ --> tests/compile-fail/duplicate_word_on_variant.rs:5:21
+ |
+5 | #[darling(word, word)]
+ | ^^^^
diff --git a/tests/compile-fail/flatten_meta_conflicts.rs b/tests/compile-fail/flatten_meta_conflicts.rs
new file mode 100644
index 0000000..62543e7
--- /dev/null
+++ b/tests/compile-fail/flatten_meta_conflicts.rs
@@ -0,0 +1,21 @@
+use darling::FromMeta;
+
+#[derive(FromMeta)]
+struct Inner {
+ left: String,
+ right: String,
+}
+
+#[derive(FromMeta)]
+struct Outer {
+ #[darling(flatten, multiple, with = demo, skip = true)]
+ field: Inner,
+}
+
+#[derive(FromMeta)]
+struct ThisIsFine {
+ #[darling(flatten, multiple = false)]
+ field: Inner,
+}
+
+fn main() {}
diff --git a/tests/compile-fail/flatten_meta_conflicts.stderr b/tests/compile-fail/flatten_meta_conflicts.stderr
new file mode 100644
index 0000000..6fa4c28
--- /dev/null
+++ b/tests/compile-fail/flatten_meta_conflicts.stderr
@@ -0,0 +1,17 @@
+error: `flatten` and `multiple` cannot be used together
+ --> tests/compile-fail/flatten_meta_conflicts.rs:11:24
+ |
+11 | #[darling(flatten, multiple, with = demo, skip = true)]
+ | ^^^^^^^^
+
+error: `flatten` and `with` cannot be used together
+ --> tests/compile-fail/flatten_meta_conflicts.rs:11:34
+ |
+11 | #[darling(flatten, multiple, with = demo, skip = true)]
+ | ^^^^
+
+error: `flatten` and `skip` cannot be used together
+ --> tests/compile-fail/flatten_meta_conflicts.rs:11:47
+ |
+11 | #[darling(flatten, multiple, with = demo, skip = true)]
+ | ^^^^
diff --git a/tests/compile-fail/flatten_multiple_fields.rs b/tests/compile-fail/flatten_multiple_fields.rs
new file mode 100644
index 0000000..a093054
--- /dev/null
+++ b/tests/compile-fail/flatten_multiple_fields.rs
@@ -0,0 +1,28 @@
+//! Test that multiple fields cannot be marked `flatten` at once.
+
+use darling::{FromDeriveInput, FromMeta};
+
+#[derive(FromMeta)]
+struct Inner {
+ left: String,
+ right: String,
+}
+
+#[derive(FromMeta)]
+pub struct Example {
+ #[darling(flatten)]
+ first: Inner,
+ #[darling(flatten)]
+ last: Inner,
+}
+
+#[derive(FromDeriveInput)]
+pub struct FdiExample {
+ ident: syn::Ident,
+ #[darling(flatten)]
+ first: Inner,
+ #[darling(flatten)]
+ last: Inner,
+}
+
+fn main() {}
diff --git a/tests/compile-fail/flatten_multiple_fields.stderr b/tests/compile-fail/flatten_multiple_fields.stderr
new file mode 100644
index 0000000..7750bfb
--- /dev/null
+++ b/tests/compile-fail/flatten_multiple_fields.stderr
@@ -0,0 +1,23 @@
+error: `#[darling(flatten)]` can only be applied to one field
+ --> tests/compile-fail/flatten_multiple_fields.rs:13:15
+ |
+13 | #[darling(flatten)]
+ | ^^^^^^^
+
+error: `#[darling(flatten)]` can only be applied to one field
+ --> tests/compile-fail/flatten_multiple_fields.rs:15:15
+ |
+15 | #[darling(flatten)]
+ | ^^^^^^^
+
+error: `#[darling(flatten)]` can only be applied to one field
+ --> tests/compile-fail/flatten_multiple_fields.rs:22:15
+ |
+22 | #[darling(flatten)]
+ | ^^^^^^^
+
+error: `#[darling(flatten)]` can only be applied to one field
+ --> tests/compile-fail/flatten_multiple_fields.rs:24:15
+ |
+24 | #[darling(flatten)]
+ | ^^^^^^^
diff --git a/tests/compile-fail/not_impl_from_meta.rs b/tests/compile-fail/not_impl_from_meta.rs
new file mode 100644
index 0000000..f27d716
--- /dev/null
+++ b/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/tests/compile-fail/not_impl_from_meta.stderr b/tests/compile-fail/not_impl_from_meta.stderr
new file mode 100644
index 0000000..a166a6f
--- /dev/null
+++ b/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/tests/compile-fail/skip_field_not_impl_default.rs b/tests/compile-fail/skip_field_not_impl_default.rs
new file mode 100644
index 0000000..f0d44c7
--- /dev/null
+++ b/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/tests/compile-fail/skip_field_not_impl_default.stderr b/tests/compile-fail/skip_field_not_impl_default.stderr
new file mode 100644
index 0000000..de46982
--- /dev/null
+++ b/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/tests/compile-fail/word_on_wrong_variant_type.rs b/tests/compile-fail/word_on_wrong_variant_type.rs
new file mode 100644
index 0000000..4bf99ca
--- /dev/null
+++ b/tests/compile-fail/word_on_wrong_variant_type.rs
@@ -0,0 +1,10 @@
+use darling::FromMeta;
+
+#[derive(FromMeta)]
+enum Meta {
+ Unit,
+ #[darling(word)]
+ NotUnit(String)
+}
+
+fn main() {}
diff --git a/tests/compile-fail/word_on_wrong_variant_type.stderr b/tests/compile-fail/word_on_wrong_variant_type.stderr
new file mode 100644
index 0000000..4193727
--- /dev/null
+++ b/tests/compile-fail/word_on_wrong_variant_type.stderr
@@ -0,0 +1,5 @@
+error: Unexpected field: `word`. `#[darling(word)]` can only be applied to a unit variant
+ --> tests/compile-fail/word_on_wrong_variant_type.rs:6:15
+ |
+6 | #[darling(word)]
+ | ^^^^
diff --git a/tests/compiletests.rs b/tests/compiletests.rs
new file mode 100644
index 0000000..00a5b32
--- /dev/null
+++ b/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/tests/computed_bound.rs b/tests/computed_bound.rs
new file mode 100644
index 0000000..abdb102
--- /dev/null
+++ b/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/tests/custom_bound.rs b/tests/custom_bound.rs
new file mode 100644
index 0000000..312f147
--- /dev/null
+++ b/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/tests/defaults.rs b/tests/defaults.rs
new file mode 100644
index 0000000..b74c745
--- /dev/null
+++ b/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/tests/enums_default.rs b/tests/enums_default.rs
new file mode 100644
index 0000000..fa6829e
--- /dev/null
+++ b/tests/enums_default.rs
@@ -0,0 +1,44 @@
+use darling::{FromDeriveInput, FromMeta};
+use syn::parse_quote;
+
+#[derive(Debug, FromMeta, PartialEq, Eq)]
+enum Dolor {
+ Sit,
+ #[darling(word)]
+ Amet,
+}
+
+impl Default for Dolor {
+ fn default() -> Self {
+ Dolor::Sit
+ }
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(hello))]
+struct Receiver {
+ #[darling(default)]
+ example: Dolor,
+}
+
+#[test]
+fn missing_meta() {
+ let di = Receiver::from_derive_input(&parse_quote! {
+ #[hello]
+ struct Example;
+ })
+ .unwrap();
+
+ assert_eq!(Dolor::Sit, di.example);
+}
+
+#[test]
+fn empty_meta() {
+ let di = Receiver::from_derive_input(&parse_quote! {
+ #[hello(example)]
+ struct Example;
+ })
+ .unwrap();
+
+ assert_eq!(Dolor::Amet, di.example);
+}
diff --git a/tests/enums_newtype.rs b/tests/enums_newtype.rs
new file mode 100644
index 0000000..c2c4ec9
--- /dev/null
+++ b/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/tests/enums_struct.rs b/tests/enums_struct.rs
new file mode 100644
index 0000000..cae4cd5
--- /dev/null
+++ b/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/tests/enums_unit.rs b/tests/enums_unit.rs
new file mode 100644
index 0000000..34e0135
--- /dev/null
+++ b/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/tests/error.rs b/tests/error.rs
new file mode 100644
index 0000000..7274e40
--- /dev/null
+++ b/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/tests/flatten.rs b/tests/flatten.rs
new file mode 100644
index 0000000..d49373a
--- /dev/null
+++ b/tests/flatten.rs
@@ -0,0 +1,181 @@
+use darling::{util::Flag, FromDeriveInput, FromMeta};
+use proc_macro2::Ident;
+use syn::parse_quote;
+
+#[derive(FromMeta)]
+struct Vis {
+ public: Flag,
+ private: Flag,
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(sample))]
+struct Example {
+ ident: Ident,
+ label: String,
+ #[darling(flatten)]
+ visibility: Vis,
+}
+
+#[test]
+fn happy_path() {
+ let di = Example::from_derive_input(&parse_quote! {
+ #[sample(label = "Hello", public)]
+ struct Demo {}
+ });
+
+ let parsed = di.unwrap();
+ assert_eq!(parsed.ident, "Demo");
+ assert_eq!(&parsed.label, "Hello");
+ assert!(parsed.visibility.public.is_present());
+ assert!(!parsed.visibility.private.is_present());
+}
+
+#[test]
+fn unknown_field_errors() {
+ let errors = Example::from_derive_input(&parse_quote! {
+ #[sample(label = "Hello", republic)]
+ struct Demo {}
+ })
+ .map(|_| "Should have failed")
+ .unwrap_err();
+
+ assert_eq!(errors.len(), 1);
+}
+
+/// This test demonstrates flatten being used recursively.
+/// Fields are expected to be consumed by the outermost matching struct.
+#[test]
+fn recursive_flattening() {
+ #[derive(FromMeta)]
+ struct Nested2 {
+ above: isize,
+ below: isize,
+ port: Option<isize>,
+ }
+
+ #[derive(FromMeta)]
+ struct Nested1 {
+ port: isize,
+ starboard: isize,
+ #[darling(flatten)]
+ z_axis: Nested2,
+ }
+
+ #[derive(FromMeta)]
+ struct Nested0 {
+ fore: isize,
+ aft: isize,
+ #[darling(flatten)]
+ cross_section: Nested1,
+ }
+
+ #[derive(FromDeriveInput)]
+ #[darling(attributes(boat))]
+ struct BoatPosition {
+ #[darling(flatten)]
+ pos: Nested0,
+ }
+
+ let parsed = BoatPosition::from_derive_input(&parse_quote! {
+ #[boat(fore = 1, aft = 1, port = 10, starboard = 50, above = 20, below = -3)]
+ struct Demo;
+ })
+ .unwrap();
+
+ assert_eq!(parsed.pos.fore, 1);
+ assert_eq!(parsed.pos.aft, 1);
+
+ assert_eq!(parsed.pos.cross_section.port, 10);
+ assert_eq!(parsed.pos.cross_section.starboard, 50);
+
+ assert_eq!(parsed.pos.cross_section.z_axis.above, 20);
+ assert_eq!(parsed.pos.cross_section.z_axis.below, -3);
+ // This should be `None` because the `port` field in `Nested1` consumed
+ // the field before the leftovers were passed to `Nested2::from_list`.
+ assert_eq!(parsed.pos.cross_section.z_axis.port, None);
+}
+
+/// This test confirms that a collection - in this case a HashMap - can
+/// be used with `flatten`.
+#[test]
+fn flattening_into_hashmap() {
+ #[derive(FromDeriveInput)]
+ #[darling(attributes(ca))]
+ struct Catchall {
+ hello: String,
+ volume: usize,
+ #[darling(flatten)]
+ others: std::collections::HashMap<String, String>,
+ }
+
+ let parsed = Catchall::from_derive_input(&parse_quote! {
+ #[ca(hello = "World", volume = 10, first_name = "Alice", second_name = "Bob")]
+ struct Demo;
+ })
+ .unwrap();
+
+ assert_eq!(parsed.hello, "World");
+ assert_eq!(parsed.volume, 10);
+ assert_eq!(parsed.others.len(), 2);
+}
+
+#[derive(FromMeta)]
+#[allow(dead_code)]
+struct Person {
+ first: String,
+ last: String,
+ parent: Option<Box<Person>>,
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(v))]
+#[allow(dead_code)]
+struct Outer {
+ #[darling(flatten)]
+ owner: Person,
+ #[darling(default)]
+ blast: bool,
+}
+
+/// This test makes sure that field names from parent structs are not inappropriately
+/// offered as alternates for unknown field errors in child structs.
+///
+/// A naive implementation that tried to offer all the flattened fields for "did you mean"
+/// could inspect all errors returned by the flattened field's `from_list` call and add the
+/// parent's field names as alternates to all unknown field errors.
+///
+/// THIS WOULD BE INCORRECT. Those unknown field errors may have already come from
+/// child fields within the flattened struct, where the parent's field names are not valid.
+#[test]
+fn do_not_suggest_invalid_alts() {
+ let errors = Outer::from_derive_input(&parse_quote! {
+ #[v(first = "Hello", last = "World", parent(first = "Hi", last = "Earth", blasts = "off"))]
+ struct Demo;
+ })
+ .map(|_| "Should have failed")
+ .unwrap_err()
+ .to_string();
+
+ assert!(
+ !errors.contains("`blast`"),
+ "Should not contain `blast`: {}",
+ errors
+ );
+}
+
+#[test]
+fn suggest_valid_parent_alts() {
+ let errors = Outer::from_derive_input(&parse_quote! {
+ #[v(first = "Hello", bladt = false, last = "World", parent(first = "Hi", last = "Earth"))]
+ struct Demo;
+ })
+ .map(|_| "Should have failed")
+ .unwrap_err()
+ .to_string();
+ assert!(
+ errors.contains("`blast`"),
+ "Should contain `blast` as did-you-mean suggestion: {}",
+ errors
+ );
+}
diff --git a/tests/flatten_error_accumulation.rs b/tests/flatten_error_accumulation.rs
new file mode 100644
index 0000000..5cd6280
--- /dev/null
+++ b/tests/flatten_error_accumulation.rs
@@ -0,0 +1,45 @@
+use darling::{util::Flag, Error, FromDeriveInput, FromMeta};
+use proc_macro2::Ident;
+use syn::parse_quote;
+
+#[derive(FromMeta)]
+#[darling(and_then = Self::validate)]
+struct Vis {
+ public: Flag,
+ private: Flag,
+}
+
+impl Vis {
+ fn validate(self) -> darling::Result<Self> {
+ if self.public.is_present() && self.private.is_present() {
+ return Err(Error::custom("Cannot be both public and private"));
+ }
+
+ Ok(self)
+ }
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(sample))]
+#[allow(dead_code)]
+struct Example {
+ ident: Ident,
+ label: String,
+ volume: usize,
+ #[darling(flatten)]
+ visibility: Vis,
+}
+
+#[test]
+fn many_errors() {
+ let e = Example::from_derive_input(&parse_quote! {
+ #[sample(volume = 10, public, private)]
+ struct Demo {}
+ })
+ .map(|_| "Should have failed")
+ .unwrap_err();
+
+ // We are expecting an error from the Vis::validate method and an error for the
+ // missing `label` field.
+ assert_eq!(e.len(), 2);
+}
diff --git a/tests/flatten_from_field.rs b/tests/flatten_from_field.rs
new file mode 100644
index 0000000..3338f04
--- /dev/null
+++ b/tests/flatten_from_field.rs
@@ -0,0 +1,84 @@
+use darling::{ast, util::Ignored, FromDeriveInput, FromField, FromMeta};
+use proc_macro2::{Ident, Span};
+use syn::parse_quote;
+
+#[derive(FromMeta)]
+struct Vis {
+ #[darling(default)]
+ public: bool,
+ #[darling(default)]
+ private: bool,
+}
+
+#[derive(FromField)]
+#[darling(attributes(v))]
+struct Field {
+ ident: Option<Ident>,
+ example: Option<String>,
+ #[darling(flatten)]
+ visibility: Vis,
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(v))]
+struct Input {
+ data: ast::Data<Ignored, Field>,
+}
+
+#[test]
+fn field_flattens() {
+ let di = Input::from_derive_input(&parse_quote! {
+ struct Demo {
+ #[v(public, example = "world")]
+ hello: String
+ }
+ })
+ .unwrap();
+
+ let fields = di.data.take_struct().unwrap();
+ let first_field = fields.into_iter().next().unwrap();
+ assert_eq!(
+ first_field.ident,
+ Some(Ident::new("hello", Span::call_site()))
+ );
+ assert!(first_field.visibility.public);
+ assert!(!first_field.visibility.private);
+ assert_eq!(first_field.example.unwrap(), "world");
+}
+
+#[test]
+fn field_flattens_with_no_field_level_attributes() {
+ let di = Input::from_derive_input(&parse_quote! {
+ struct Demo {
+ hello: String
+ }
+ })
+ .unwrap();
+
+ let fields = di.data.take_struct().unwrap();
+ let first_field = fields.into_iter().next().unwrap();
+ assert_eq!(
+ first_field.ident,
+ Some(Ident::new("hello", Span::call_site()))
+ );
+ assert!(!first_field.visibility.public);
+ assert!(!first_field.visibility.private);
+ assert_eq!(first_field.example, None);
+}
+
+#[test]
+fn field_flattens_across_attributes() {
+ let di = Input::from_derive_input(&parse_quote! {
+ struct Demo {
+ #[v(public)]
+ #[v(private)]
+ hello: String
+ }
+ })
+ .unwrap();
+
+ let fields = di.data.take_struct().unwrap();
+ let first_field = fields.into_iter().next().unwrap();
+ assert!(first_field.visibility.public);
+ assert!(first_field.visibility.private);
+}
diff --git a/tests/from_generics.rs b/tests/from_generics.rs
new file mode 100644
index 0000000..e2cc99e
--- /dev/null
+++ b/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 param, though we don't do anything with it.
+ assert!(rec.generics.params[0].as_lifetime_param().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/tests/from_meta.rs b/tests/from_meta.rs
new file mode 100644
index 0000000..886465c
--- /dev/null
+++ b/tests/from_meta.rs
@@ -0,0 +1,109 @@
+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()
+ );
+}
+
+/// Tests behavior of FromMeta implementation for enums.
+mod enum_impl {
+ use darling::{Error, FromMeta};
+ use syn::parse_quote;
+
+ /// A playback volume.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, FromMeta)]
+ enum Volume {
+ Normal,
+ Low,
+ High,
+ #[darling(rename = "dB")]
+ Decibels(u8),
+ }
+
+ #[test]
+ fn string_for_unit_variant() {
+ let volume = Volume::from_string("low").unwrap();
+ assert_eq!(volume, Volume::Low);
+ }
+
+ #[test]
+ fn single_value_list() {
+ let unit_variant = Volume::from_list(&[parse_quote!(high)]).unwrap();
+ assert_eq!(unit_variant, Volume::High);
+
+ let newtype_variant = Volume::from_list(&[parse_quote!(dB = 100)]).unwrap();
+ assert_eq!(newtype_variant, Volume::Decibels(100));
+ }
+
+ #[test]
+ fn empty_list_errors() {
+ let err = Volume::from_list(&[]).unwrap_err();
+ assert_eq!(err.to_string(), Error::too_few_items(1).to_string());
+ }
+
+ #[test]
+ fn multiple_values_list_errors() {
+ let err = Volume::from_list(&[parse_quote!(low), parse_quote!(dB = 20)]).unwrap_err();
+ assert_eq!(err.to_string(), Error::too_many_items(1).to_string());
+ }
+}
diff --git a/tests/from_type_param.rs b/tests/from_type_param.rs
new file mode 100644
index 0000000..50ec306
--- /dev/null
+++ b/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(&params[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(&params[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/tests/from_type_param_default.rs b/tests/from_type_param_default.rs
new file mode 100644
index 0000000..9d65665
--- /dev/null
+++ b/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(&params[0]);
+ let lorem = Lorem::from_type_param(ty).unwrap();
+ assert_eq!(lorem.foo, true);
+ assert_eq!(lorem.bar, None);
+ }
+
+ {
+ let ty = extract_type(&params[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(&params[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/tests/from_variant.rs b/tests/from_variant.rs
new file mode 100644
index 0000000..e89b8ff
--- /dev/null
+++ b/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/tests/generics.rs b/tests/generics.rs
new file mode 100644
index 0000000..ba65781
--- /dev/null
+++ b/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/tests/happy_path.rs b/tests/happy_path.rs
new file mode 100644
index 0000000..a56aee7
--- /dev/null
+++ b/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/tests/hash_map.rs b/tests/hash_map.rs
new file mode 100644
index 0000000..881cd1c
--- /dev/null
+++ b/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.meta;
+ 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/tests/multiple.rs b/tests/multiple.rs
new file mode 100644
index 0000000..b2243e6
--- /dev/null
+++ b/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/tests/newtype.rs b/tests/newtype.rs
new file mode 100644
index 0000000..10d0238
--- /dev/null
+++ b/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/tests/skip.rs b/tests/skip.rs
new file mode 100644
index 0000000..f930ca5
--- /dev/null
+++ b/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/tests/split_declaration.rs b/tests/split_declaration.rs
new file mode 100644
index 0000000..8db11d0
--- /dev/null
+++ b/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/tests/suggestions.rs b/tests/suggestions.rs
new file mode 100644
index 0000000..862084f
--- /dev/null
+++ b/tests/suggestions.rs
@@ -0,0 +1,44 @@
+#![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,
+ // This field is included to make sure that skipped fields aren't suggested.
+ #[darling(skip)]
+ amet: bool,
+}
+
+#[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"));
+}
+
+#[test]
+fn dont_suggest_skipped_field() {
+ let input: syn::DeriveInput = parse_quote! {
+ #[suggest(ipsum = "Hello", dolor(sit), amt)]
+ pub struct Foo;
+ };
+
+ let result = Lorem::from_derive_input(&input).unwrap_err();
+ assert_eq!(1, result.len());
+ assert!(!format!("{}", result).contains("amet"));
+}
diff --git a/tests/supports.rs b/tests/supports.rs
new file mode 100644
index 0000000..d6c7556
--- /dev/null
+++ b/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/tests/unsupported_attributes.rs b/tests/unsupported_attributes.rs
new file mode 100644
index 0000000..c0143ce
--- /dev/null
+++ b/tests/unsupported_attributes.rs
@@ -0,0 +1,31 @@
+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.
+/// 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: Debug)]
+ #[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 2:
+ // - The parsing error caused by a where-clause body where it doesn't belong
+ // - The missing `st` value because the parsing failure blocked that attribute from
+ // being read.
+ assert_eq!(2, errors.len());
+}