update: arbitrary->1.4.1, derive_arbitrary->1.4.1
Bug: 388276501
Test: mm in android-crates-io
Change-Id: Id7c7974fc1bc2dfeb5873a3e315d34f25af4ef25
diff --git a/crates/arbitrary/.android-checksum.json b/crates/arbitrary/.android-checksum.json
new file mode 100644
index 0000000..68a31d9
--- /dev/null
+++ b/crates/arbitrary/.android-checksum.json
@@ -0,0 +1 @@
+{"package":null,"files":{".cargo-checksum.json":"de362fff988b9f1d462a9ab7cc592643ef52242a24e18a32d736e670c42c8ad0","Android.bp":"4bc1007f1349c0dd73e3bf4bb5bdfabb0c6fab62d5482eb9eef4f03afafc8402","CHANGELOG.md":"acff0f841ed9aa0f4e9e05ad406176a8029d7daf2044b1d0588bef0f6abef4f6","Cargo.lock":"cf062320cb16dc86c0a74589bdbef5db606c401e6a944e9b3b3461ade3c48f98","Cargo.toml":"d13ac9fac79450ced958fada3a81eae2552108d88c44765019cdd81a1ec9be5e","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"3b270c5247fc2265dac124e9ada8609aa00c03289761bafa13c741aee60a504a","METADATA":"5ed74da37fc713266c41540eecc4caabfa53a24e0a48b7c07db1233b5494d833","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","NOTICE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","README.md":"7c4cd865df07e51893f62d4284fd38060a80b4299506bde81b93a47353a25cd5","TEST_MAPPING":"c0543deb187cac29e27cf8953de57a0697a6ef1710cfad301c6152bbb256b424","cargo_embargo.json":"8c3d9f3c87e4f3b45d16936248c08499f13757a6f5ee7aa48069e8462c2e867b","examples/derive_enum.rs":"f0da81007fc5cf755d4367bc5bd4b01b8bf681e71d252551228f12a2cfba8b09","publish.sh":"950f3f763afd7c09855814e7fb002e438f14530b043fb2a807578228b2829802","src/error.rs":"abdcfad0bc9cecc2ca2c062512ed8e971d26ff560de632d8b5b68b93ca1eca39","src/foreign/alloc/borrow.rs":"a032b34c3e7cb33cc1998023e4075262ef03e6232803ac6860eabac8142c1aaf","src/foreign/alloc/boxed.rs":"f7a8cf03d7fc28e4fc70b5fc2213e250bea4587fa43452f956d8ed20d7d64cf6","src/foreign/alloc/collections/binary_heap.rs":"8d1aba92d4d7475316527217e4d6325494dfffb7255ee36f57e709b1fc2e8fb0","src/foreign/alloc/collections/btree_map.rs":"7d15e4acfbecd9152b659a2c515a5703b478f81d4a43c5de6df4de0b46336609","src/foreign/alloc/collections/btree_set.rs":"89bf8b9d9c74649714bba5bbfa6184491cfeeece2ce31d9aa91320b305d6aff6","src/foreign/alloc/collections/linked_list.rs":"ec297c92f2416624efe93e7fcf27d03eeb67c460dda6b765a033dc5291c2e602","src/foreign/alloc/collections/mod.rs":"8f35b9a12cb837c3344c4501ca31497e6922746d83ee3a8824739bcf521c2c28","src/foreign/alloc/collections/vec_deque.rs":"960d10c116d920cd28980a1b54810aba000cd7bcf3c40ad06183d7f0f62a13c4","src/foreign/alloc/ffi/c_str.rs":"52dd537b1bf2c11062482c27eed5ae78610699c0963b2faf9c5a10804e062cc2","src/foreign/alloc/ffi/mod.rs":"4ea7541a08577031bb0af4dd5ca36187d3462a125e85e3ed1014c51eee1a7a75","src/foreign/alloc/mod.rs":"3537453799caa122bd9138ea85c51b97997c80a22606f9530a4d786dbeb8a015","src/foreign/alloc/rc.rs":"50b8318e35e66b912e718e894f36c6a0c93e253158ffc6b05fc7ed77d6b207d4","src/foreign/alloc/string.rs":"032de94b1b28ba16af388d8ca89af9dbf2a4ec2f950d00aeaa6ea36ebce915ef","src/foreign/alloc/sync.rs":"8f1fbc6f7412ad43e114cc73eb309d320351bdb4083a4c091fabe886169291de","src/foreign/alloc/vec.rs":"40fa7f7bd8a2e2eeef7cd53a0f2f1df34663a30f68712b7a077cc55df8b04fe7","src/foreign/core/array.rs":"0c13d9acd119af393da2dff4ba8478eb1cdabdbef76a36fa28eef5f269243f7d","src/foreign/core/bool.rs":"7de87766f4753f4abe97e85d6a4f0143b0269693ba94fd7ebfb13cdd1cb4e386","src/foreign/core/cell.rs":"6137a6d85854a2731d76baa81da53b7d1eb1244bbcf6d301fe941237ae9ef8d4","src/foreign/core/char.rs":"415752a792acea61337d0240e7a3ddf6baf924450bc8b3e5a1dbd40abe75fcae","src/foreign/core/iter.rs":"fbe5714ead7cd33b84826bc07249fc4a12b646a6b84cd4ae509b49ca0557f46a","src/foreign/core/marker.rs":"bbe0e24a17e0be0f0adc1bd16744ae8fa5652f4bb172e517bf4fab80b27fdf12","src/foreign/core/mod.rs":"1ad61fc51ca3f0432a37718745c75a2072d3f614a12d13b0c59debb002215caf","src/foreign/core/num.rs":"7c378a94eb223bcf121a0dc2d2efd8f9659fac32a92df59b6c35cc9898b6644c","src/foreign/core/ops.rs":"e5f1e8c2e5f0192cb939d1b47da33075a0083ddfd5ecab1650bf48300484ba84","src/foreign/core/option.rs":"07774d7e1eacebbb53b0bf21b4887fd0c757222f3df10546a3f8e2e6d72077c4","src/foreign/core/result.rs":"d945e5cf50aca2da94940de3bc6f2741e1a9c862e2ab0f2485f8f151538504a5","src/foreign/core/slice.rs":"d48412ee49abcec348f4737d908396bf89db3905720598ca93af5979d9377c4c","src/foreign/core/str.rs":"63550f53d73ef952dab534627c5c5e59f7f3cb3d3c05a35e992dae90ce3362df","src/foreign/core/sync/atomic.rs":"e02d231885055fee90f7e41af6b09e503b998ce471b71cd96f6e665ed4bbff4c","src/foreign/core/sync/mod.rs":"4281ca1fc1f3cd553c40f808a6b84b0ba42fa509a609eb9b8dea7720d72aec9f","src/foreign/core/time.rs":"730fe349eead9530ea50b7a1cf0a32e05570c8cce28bf20ae6680d94945a6f09","src/foreign/core/tuple.rs":"bcf0676666495f8cb62b38f708dd47a355cca3f113a50ec72ddb91e77eaf95ae","src/foreign/core/unit.rs":"4945acf688af425358558124925362427c8214b6f49d4b0465bc81c603b53425","src/foreign/mod.rs":"f5e245c3206e1218f524c6c845331a696b1def9e2e6f37fa3470f3232cc5cb67","src/foreign/std/collections/hash_map.rs":"9a9bcfc2399ef048e9a2551bb68babaab42ef73a4c5027a5b2c672838944988f","src/foreign/std/collections/hash_set.rs":"79d5bbc7321aab2efa7a74ea0f2fa854878e2a3cf5bbf6b0c0053bfd1d0a6b9e","src/foreign/std/collections/mod.rs":"4cd786b68ed9c98003064e69149044bc3163f778f377818810864da7d7b813ce","src/foreign/std/ffi/c_str.rs":"c89cc4a5371d0f533f5841b2e9dab3d01650a796874532af43cca673665ca65d","src/foreign/std/ffi/mod.rs":"9e339f96466034a879bd1b5ce8ddebc31920b29da3b2179edbd654cffd56d8f4","src/foreign/std/ffi/os_str.rs":"f5c4430c14d1dffb64284754c3cc7f1f64191055b9a82a1433643ddea09b24b8","src/foreign/std/mod.rs":"ea192c8c6d13eada32ee715d6b8d8841f339ab8efd42140d4326bfb3a079e7de","src/foreign/std/net.rs":"804795d8ab431adcd441df7228b362a4a0b26b3fbbd08059304be248237607d9","src/foreign/std/path.rs":"0e2dfec57c6ac2552ee0f5a777c7ae5492b565591a87fba90a17a60da902a987","src/foreign/std/sync.rs":"cfa86eb3727c1252fcdd827dd49ad87d8d2d0de605adcc12f08ed26a7d9b6f06","src/lib.rs":"e622864f873299ae4379f840d796a7114375291ebbad11dda4724c5cfe6b835d","src/size_hint.rs":"5dcf145a1257dce08d2bae05d2f6bc8c0c31b552410f688154936a89711e5d0d","src/tests.rs":"5e00b3b46070bcfd3e4a93db9dd98959e076ad6426042cc6111b7e1125f814e2","src/unstructured.rs":"5814ccc5c11b505d5f7c8ffcd67bc75c3da81e3d6cd61af2f7d1bfc59f533d65","tests/bound.rs":"97e331d5fad3caca676151bb5a5c30924e29ddf55c98f99445e379278d8fe772","tests/derive.rs":"632a230bb9ed3e4720ca81821c4a628f812d8fa8b1b65fb51fa34fbbe0311214","tests/path.rs":"19926dcdfc9048b0ce044b77eb7385904a4cc4b5399df7480fa1f72c7fec4c2a"}}
\ No newline at end of file
diff --git a/crates/arbitrary/.cargo-checksum.json b/crates/arbitrary/.cargo-checksum.json
index b53838b..ce9040e 100644
--- a/crates/arbitrary/.cargo-checksum.json
+++ b/crates/arbitrary/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"CHANGELOG.md":"2db35de47ad7a0edb380d919a7506e11028a718a4bff5343f8ddb5727c4abd5f","Cargo.lock":"1b521507672c462c7cf7d9fcbb89e1eebf3e4c84400c22ab217c6b2392911d01","Cargo.toml":"7961c38666b4e00ff7cc94fb2d835da19fa579856b6868118583442a72bb4685","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"4bce3213c030f51154228986fa9b82881a5a979fabe6c65f8e6cd251d324a020","examples/derive_enum.rs":"ef93268fbed2b70c177bebf5757ef1ee0ca5ba0cb0d335d0989a81725463f8c9","publish.sh":"752e221bdd960666b127df15effddd3d789ff3f1762498961fc79ae99f9a27f1","src/error.rs":"33d74ddd002dd8cf73514c93b7e1ecb604718bea69e53f619bb7a87c7f8d71f2","src/lib.rs":"63af26735ee483ada0ebc7698761e6e5278436e643a81628a865b883ed4662f9","src/size_hint.rs":"9762b183f8277ee4955fe5b22552961744b6237286758161a551f904ef43e3eb","src/unstructured.rs":"0a0ab0f889796bee2e1abe59f7b91c87f0a6991c155c2250a093f736ced4e4e6","tests/derive.rs":"5aef1e3a8b709a3e3e0b154ecd51e34bb4d5f8968291ee691326897da58d9658","tests/path.rs":"7e3e5fa26d5671ac19e99ba010a2fa0894fafe996c5d28ee587919e486d7c7c1"},"package":"3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad"}
\ No newline at end of file
+{"files":{"CHANGELOG.md":"9efc0ce89509a05f97a8a8fb0e0b63a8e161a171dfb2012568d0578cf08c6653","Cargo.lock":"7f28adbc92015080ac31f75f26e5d3ef0bd6ff479995e63c58435d5c609c3734","Cargo.toml":"846a1554e7ad0841d2d7a8b5f13b419b05eaeaa158e75fdb9a5e6a5b4afafb66","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"5a1c78cfd5295f86d11bf9baf39e7190366b94a2cfb39bb6ad59b26b66addd52","examples/derive_enum.rs":"45d11d8cd0461716b276e2df4a5b31e7ae7ece36f05d42a0d4c055a5e4f02ab1","publish.sh":"752e221bdd960666b127df15effddd3d789ff3f1762498961fc79ae99f9a27f1","src/error.rs":"8cd2f06dc455d28ee00ac8305ca5f53764e8205e280aa3652988497859163e99","src/foreign/alloc/borrow.rs":"2741eea43a139e7c83b92ab237a60f421224c539ef90ae3125be34b1e6943e54","src/foreign/alloc/boxed.rs":"2ea6fe8795b644de7785ff5373ab6728399de531c2bf50b8f501d48e15dc92db","src/foreign/alloc/collections/binary_heap.rs":"69e648323514313a6f9b6e24b1dbe03d3b24830608f04ad8acac043e6eb44a2b","src/foreign/alloc/collections/btree_map.rs":"158e9e611cb7b03b3190ee8bbebe855cf2291bf14c2adc774d4e6edf5ec47650","src/foreign/alloc/collections/btree_set.rs":"16ad32b9edaa871c046a2543614eb18513801fd880f8926343c2cc74ee7251a4","src/foreign/alloc/collections/linked_list.rs":"cad35cd65f824ef4f0931fa6c200d737b2ac56c3940321a7c7c7b7d9b2d86f1f","src/foreign/alloc/collections/mod.rs":"e81c20370de8903f79d53dd89552c00d3ce6ff12280b379a5ab8d8d1adcecaa5","src/foreign/alloc/collections/vec_deque.rs":"0821db085c8303f8f9e4a316a6dbb5529330488ed06c19284a22a6ef72313270","src/foreign/alloc/ffi/c_str.rs":"16228949035611dfa6522fdc8f25f5afc188eef74febbf06769e9edb88a8430f","src/foreign/alloc/ffi/mod.rs":"9e612467614f7a53369b11278d624bca71f1f44156fcb58d891c7d06f6f1425f","src/foreign/alloc/mod.rs":"27e0cdebd51ee3626ac7ae0b6926c25fdacc9c74b337318d35573d33cc9996b9","src/foreign/alloc/rc.rs":"d17102fc5573461b33b995b5e69553f4d1115f40d70a500c6b86cdf1af766e48","src/foreign/alloc/string.rs":"4c67871b59fb0ce1c13c54b2df2e2054cf02986152bcead7a701fc272ded3cbd","src/foreign/alloc/sync.rs":"e95b2d61962247a03524583aa5ecd1ad5abeb2cfe6590b3ccf7f26649f1287ac","src/foreign/alloc/vec.rs":"57ca9806b9381d6b53e46a0b15fa97c1c698ef950686a1502d0b034656a3f23e","src/foreign/core/array.rs":"1d8c7d794f9990ea43166744a7b25937d43f5f438d0a2c0ab086d88ea07258bd","src/foreign/core/bool.rs":"52a7a7e0dbf613c9f96bde05923b8e766cde41493c6bae5a45bf9c1a104d9590","src/foreign/core/cell.rs":"b594f71084136f441d49340c74e5df734df9f6d91a3fd9945d96595c440f0a2b","src/foreign/core/char.rs":"6705cb3025ca782190e15089689d8cd248b02941bb43fcf4cbf3555c5b5a6e8f","src/foreign/core/iter.rs":"8e5ea3c9885db203cdfb55585c7f6d46f70aa626188abf80af58f2c96df38f53","src/foreign/core/marker.rs":"67386fd457adfabcca6c6fdfbc6883782f6ee3bdd8c344e316adc4587de886af","src/foreign/core/mod.rs":"66b354856caa1d1e2a89f999f5ad555d27cbf3b186fe8087b83e86d4a06dea65","src/foreign/core/num.rs":"294b4977e7707d274461d3029fc7b3225fe2dc4d21e0b86f01bed16b24f901b6","src/foreign/core/ops.rs":"f9bfaef1d11aac50dbefbcd2122b912999401a5a1a165caadc7c3e7aa844d61a","src/foreign/core/option.rs":"1bf99de94ac05ac18c36e1eb3c9b01fcd4e5eddb47c82fe2722c8fc327e29cb8","src/foreign/core/result.rs":"13ea4aab756af7de3e1cc1e38aa0a68045c213636809862193a047c36f13f0a7","src/foreign/core/slice.rs":"f42caa49d1a55461c9d515d386fc2b7b2bfc5f0ac8a229d5ca465af5573a453f","src/foreign/core/str.rs":"add0bc52fcc06adcf294f16bf95f3c513ce07a2fd614d8474976cbf743922e2a","src/foreign/core/sync/atomic.rs":"9be6b63f3b932355a5ae0c578e10399b920bde91be9becb640d7e416462b2631","src/foreign/core/sync/mod.rs":"a5ae8ce1fc6b462b1f57993f94bd8cf014d5375728e67dd9c24a21b66cba95c8","src/foreign/core/time.rs":"f7ef56316b1db52a7bcb1ce841862df7d9b91b3f8dad5313eb0f429d988acc9b","src/foreign/core/tuple.rs":"66e9361bec0d62d2f4214ddab266fee2258cbf52c1243f8882612861b2bd98f1","src/foreign/core/unit.rs":"1035c63c6a7757bae8940bf0fbdcc8c49a2ca2ea004d8c39666c5d0b22d921e1","src/foreign/mod.rs":"9ab162a3dc28ad93acf2a8fc2942d0f409de349a4282f42538e09669aec4aaa5","src/foreign/std/collections/hash_map.rs":"aa6c7e6e7cf4afcb207fa43f1edf95dd3ec4d78cc5fe86bf40a8b98b116d5f0f","src/foreign/std/collections/hash_set.rs":"4b36b1e6f096c8690c7c6b106505c53cd58370f9fb9b4683cd753fa7f393bda4","src/foreign/std/collections/mod.rs":"54399ce5574fd1d84b7b0cb4238fa3e898575e89a6724299be009d2172bda02e","src/foreign/std/ffi/c_str.rs":"80c4bcd6e3d79999503b50c9b82c234539cd63905c0745f9f755b91e1fe54f9a","src/foreign/std/ffi/mod.rs":"7b6b2f64278961fb440787161924427fb6f4ebc376243932ecab9409e1dab755","src/foreign/std/ffi/os_str.rs":"e20cce997e2139d51dc345375e3ad8da3385de7d46e3dc288fc4963835fcb0e6","src/foreign/std/mod.rs":"f304d0482650833ce7f3a785add71590019c3e00ce39f68740b98a966100acf3","src/foreign/std/net.rs":"e391211150bd94fc6fd7f8dac0e1e7e0454b6421f69790996efd93fcd5e277a8","src/foreign/std/path.rs":"8ef7a801595763c99a1342eeaa222a08a46e5c5770bc4a167d36cfd933ad9649","src/foreign/std/sync.rs":"bfbde00300424956e7f06df3194547c0cf2a463ec60df47b0ec90d920e063f8b","src/lib.rs":"6b429a3d1689a5d4cc9699bb8a7d53c2ff22c607f1d7eb8b2b1d271f6467a9ab","src/size_hint.rs":"2e409df461eabb90c4b3e6788e99cb9469a134bb28b95bb7faf1ca5061a6be91","src/tests.rs":"9044f1cf9fbf4879c4e91181376b52700d4dacbcdd1c792743c8abd69281d59a","src/unstructured.rs":"68987005eea45fa558398ef22c64ef526fc0480038cec5a006ccf320622bca2b","tests/bound.rs":"32b8ce8083419ecadd66e1fde4da427b437ac9e007ed9d6b5229a4ef24a5d772","tests/derive.rs":"970bee897008c0132075ff8aa14ac9610933746dabc175b626e479637972e03f","tests/path.rs":"7e3e5fa26d5671ac19e99ba010a2fa0894fafe996c5d28ee587919e486d7c7c1"},"package":"dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"}
\ No newline at end of file
diff --git a/crates/arbitrary/Android.bp b/crates/arbitrary/Android.bp
index adeeb17..183d010 100644
--- a/crates/arbitrary/Android.bp
+++ b/crates/arbitrary/Android.bp
@@ -13,55 +13,14 @@
license_text: ["LICENSE"],
}
-rust_test {
- name: "arbitrary_test_src_lib",
- host_supported: true,
- crate_name: "arbitrary",
- cargo_env_compat: true,
- cargo_pkg_version: "1.2.3",
- crate_root: "src/lib.rs",
- test_suites: ["general-tests"],
- auto_gen_config: true,
- test_options: {
- unit_test: true,
- },
- edition: "2018",
- features: [
- "derive",
- "derive_arbitrary",
- ],
- proc_macros: ["libderive_arbitrary"],
-}
-
-rust_test {
- name: "arbitrary_test_tests_derive",
- host_supported: true,
- crate_name: "derive",
- cargo_env_compat: true,
- cargo_pkg_version: "1.2.3",
- crate_root: "tests/derive.rs",
- test_suites: ["general-tests"],
- auto_gen_config: true,
- test_options: {
- unit_test: true,
- },
- edition: "2018",
- features: [
- "derive",
- "derive_arbitrary",
- ],
- rustlibs: ["libarbitrary"],
- proc_macros: ["libderive_arbitrary"],
-}
-
rust_library_rlib {
name: "libarbitrary",
host_supported: true,
crate_name: "arbitrary",
cargo_env_compat: true,
- cargo_pkg_version: "1.2.3",
+ cargo_pkg_version: "1.4.1",
crate_root: "src/lib.rs",
- edition: "2018",
+ edition: "2021",
features: [
"derive",
"derive_arbitrary",
diff --git a/crates/arbitrary/CHANGELOG.md b/crates/arbitrary/CHANGELOG.md
index 2d4bb72..eb270e6 100644
--- a/crates/arbitrary/CHANGELOG.md
+++ b/crates/arbitrary/CHANGELOG.md
@@ -20,7 +20,7 @@
### Fixed
-* (Included in `arbitrary_derive` 1.2.1) Fixed bug in Derive macro around `no_std` path uses [#131](https://github.com/rust-fuzz/arbitrary/pull/131)
+* TODO (or remove section if none)
### Security
@@ -28,6 +28,70 @@
--------------------------------------------------------------------------------
+## 1.4.0
+
+Released 2024-10-30.
+
+### Added
+
+* Added an `Arbitrary` implementation for `PhantomPinned`.
+* Added the `Unstructured::choose_iter` helper method.
+* Added `#[arbitrary(skip)]` for `enum` variants in the derive macro.
+* Added the `Arbitrary::try_size_hint` trait method.
+
+### Changed
+
+* Implement `Arbitrary` for `PhantomData<A>` even when `A` does not implement
+ `Arbitrary` and when `A` is `?Sized`.
+* Make `usize`'s underlying encoding independent of machine word size so that
+ corpora are more portable.
+
+### Fixed
+
+* Make `derive(Arbitrary)` work for local definitions of `struct Option`.
+
+--------------------------------------------------------------------------------
+
+## 1.3.2
+
+Released 2023-10-30.
+
+### Added
+
+* Added `Arbitrary` implementations for `Arc<[T]>` and
+ `Rc<[T]>`. [#160](https://github.com/rust-fuzz/arbitrary/pull/160)
+
+--------------------------------------------------------------------------------
+
+## 1.3.1
+
+Released 2023-10-11.
+
+### Fixed
+
+* Fixed an issue with generating collections of collections in
+ `arbitrary_take_rest` where `<Vec<Vec<u8>>>::arbitrary_take_rest` would never
+ generate `vec![vec![]]` for example. See
+ [#159](https://github.com/rust-fuzz/arbitrary/pull/159) for details.
+
+--------------------------------------------------------------------------------
+
+## 1.3.0
+
+Released 2023-03-13.
+
+### Added
+
+* Added the ability to manually specify derived trait bounds for
+ `Arbitrary`. See [#138](https://github.com/rust-fuzz/arbitrary/pull/138) for
+ details.
+
+### Fixed
+
+* Fixed minimal versions correctness for `syn`.
+
+--------------------------------------------------------------------------------
+
## 1.2.3
Released 2023-01-20.
@@ -69,7 +133,7 @@
## 1.1.6
-Released 2022-09-08.
+Released 2022-09-20.
### Fixed
@@ -80,7 +144,7 @@
## 1.1.5
-Released 2022-09-20.
+Released 2022-09-08.
### Added
diff --git a/crates/arbitrary/Cargo.lock b/crates/arbitrary/Cargo.lock
index 26d8335..1cf4e06 100644
--- a/crates/arbitrary/Cargo.lock
+++ b/crates/arbitrary/Cargo.lock
@@ -4,16 +4,17 @@
[[package]]
name = "arbitrary"
-version = "1.2.3"
+version = "1.4.1"
dependencies = [
"derive_arbitrary",
+ "exhaustigen",
]
[[package]]
name = "derive_arbitrary"
-version = "1.2.3"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8beee4701e2e229e8098bbdecdca12449bc3e322f137d269182fa1291e20bd00"
+checksum = "d475dfebcb4854d596b17b09f477616f80f17a550517f2b3615d8c205d5c802b"
dependencies = [
"proc-macro2",
"quote",
@@ -21,28 +22,34 @@
]
[[package]]
-name = "proc-macro2"
-version = "1.0.50"
+name = "exhaustigen"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
+checksum = "7d88f747710a968a0a32ce4c4ae90ead21dc36a06aceabbb4367452679a95eb6"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.23"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
-version = "1.0.107"
+version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
@@ -51,6 +58,6 @@
[[package]]
name = "unicode-ident"
-version = "1.0.6"
+version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
diff --git a/crates/arbitrary/Cargo.toml b/crates/arbitrary/Cargo.toml
index 9b11611..8f5a881 100644
--- a/crates/arbitrary/Cargo.toml
+++ b/crates/arbitrary/Cargo.toml
@@ -10,10 +10,10 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
+edition = "2021"
rust-version = "1.63.0"
name = "arbitrary"
-version = "1.2.3"
+version = "1.4.1"
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <[email protected]>",
@@ -22,6 +22,11 @@
"Brian L. Troutwine <[email protected]>",
"Corey Farwell <[email protected]>",
]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
description = "The trait for generating structured data from unstructured data"
documentation = "https://docs.rs/arbitrary/"
readme = "README.md"
@@ -33,18 +38,34 @@
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary/"
+[lib]
+name = "arbitrary"
+path = "src/lib.rs"
+
[[example]]
name = "derive_enum"
+path = "examples/derive_enum.rs"
required-features = ["derive"]
[[test]]
+name = "bound"
+path = "tests/bound.rs"
+
+[[test]]
name = "derive"
-path = "./tests/derive.rs"
+path = "tests/derive.rs"
required-features = ["derive"]
+[[test]]
+name = "path"
+path = "tests/path.rs"
+
[dependencies.derive_arbitrary]
-version = "1.2.3"
+version = "1.4.0"
optional = true
+[dev-dependencies.exhaustigen]
+version = "0.1.0"
+
[features]
derive = ["derive_arbitrary"]
diff --git a/crates/arbitrary/METADATA b/crates/arbitrary/METADATA
index 836167c..654a476 100644
--- a/crates/arbitrary/METADATA
+++ b/crates/arbitrary/METADATA
@@ -1,17 +1,17 @@
name: "arbitrary"
description: "The trait for generating structured data from unstructured data"
third_party {
- version: "1.2.3"
+ version: "1.4.1"
license_type: NOTICE
last_upgrade_date {
- year: 2023
- month: 2
- day: 1
+ year: 2025
+ month: 1
+ day: 7
}
homepage: "https://crates.io/crates/arbitrary"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/arbitrary/arbitrary-1.2.3.crate"
- version: "1.2.3"
+ value: "https://static.crates.io/crates/arbitrary/arbitrary-1.4.1.crate"
+ version: "1.4.1"
}
}
diff --git a/crates/arbitrary/README.md b/crates/arbitrary/README.md
index 3f5619b..c82309b 100644
--- a/crates/arbitrary/README.md
+++ b/crates/arbitrary/README.md
@@ -84,7 +84,7 @@
#[arbitrary(with = arbitrary_b)]
pub b: u8,
- // Generate `a` with a custom closure (shortuct to avoid a custom funciton)
+ // Generate `a` with a custom closure (shortuct to avoid a custom function)
#[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=64))]
pub a: u8,
}
diff --git a/crates/arbitrary/examples/derive_enum.rs b/crates/arbitrary/examples/derive_enum.rs
index c4fc9c9..3009f06 100644
--- a/crates/arbitrary/examples/derive_enum.rs
+++ b/crates/arbitrary/examples/derive_enum.rs
@@ -10,9 +10,15 @@
#[derive(Arbitrary, Debug)]
enum MyEnum {
- UnitVariant,
- TupleVariant(bool, u32),
- StructVariant { x: i8, y: (u8, i32) },
+ Unit,
+ Tuple(bool, u32),
+ Struct {
+ x: i8,
+ y: (u8, i32),
+ },
+
+ #[arbitrary(skip)]
+ Skipped(usize),
}
fn main() {
diff --git a/crates/arbitrary/src/error.rs b/crates/arbitrary/src/error.rs
index 1d3df2a..6ca8f19 100644
--- a/crates/arbitrary/src/error.rs
+++ b/crates/arbitrary/src/error.rs
@@ -1,7 +1,7 @@
use std::{error, fmt};
/// An enumeration of buffer creation errors
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// No choices were provided to the Unstructured::choose call
diff --git a/crates/arbitrary/src/foreign/alloc/borrow.rs b/crates/arbitrary/src/foreign/alloc/borrow.rs
new file mode 100644
index 0000000..b5b1e22
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/borrow.rs
@@ -0,0 +1,26 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ std::borrow::{Cow, ToOwned},
+};
+
+impl<'a, A> Arbitrary<'a> for Cow<'a, A>
+where
+ A: ToOwned + ?Sized + 'a,
+ <A as ToOwned>::Owned: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Cow::Owned)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+ size_hint::try_recursion_guard(depth, |depth| {
+ <<A as ToOwned>::Owned as Arbitrary>::try_size_hint(depth)
+ })
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/boxed.rs b/crates/arbitrary/src/foreign/alloc/boxed.rs
new file mode 100644
index 0000000..c3014a3
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/boxed.rs
@@ -0,0 +1,52 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ std::boxed::Box,
+};
+
+impl<'a, A> Arbitrary<'a> for Box<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+ size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
+ }
+}
+
+impl<'a, A> Arbitrary<'a> for Box<[A]>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
+
+impl<'a> Arbitrary<'a> for Box<str> {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <String as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_str())
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <String as Arbitrary>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/binary_heap.rs b/crates/arbitrary/src/foreign/alloc/collections/binary_heap.rs
new file mode 100644
index 0000000..25a0384
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/binary_heap.rs
@@ -0,0 +1,22 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::collections::binary_heap::BinaryHeap,
+};
+
+impl<'a, A> Arbitrary<'a> for BinaryHeap<A>
+where
+ A: Arbitrary<'a> + Ord,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/btree_map.rs b/crates/arbitrary/src/foreign/alloc/collections/btree_map.rs
new file mode 100644
index 0000000..21b93a4
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/btree_map.rs
@@ -0,0 +1,23 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::collections::btree_map::BTreeMap,
+};
+
+impl<'a, K, V> Arbitrary<'a> for BTreeMap<K, V>
+where
+ K: Arbitrary<'a> + Ord,
+ V: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/btree_set.rs b/crates/arbitrary/src/foreign/alloc/collections/btree_set.rs
new file mode 100644
index 0000000..8c6e92f
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/btree_set.rs
@@ -0,0 +1,22 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::collections::btree_set::BTreeSet,
+};
+
+impl<'a, A> Arbitrary<'a> for BTreeSet<A>
+where
+ A: Arbitrary<'a> + Ord,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/linked_list.rs b/crates/arbitrary/src/foreign/alloc/collections/linked_list.rs
new file mode 100644
index 0000000..6bf2e98
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/linked_list.rs
@@ -0,0 +1,22 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::collections::linked_list::LinkedList,
+};
+
+impl<'a, A> Arbitrary<'a> for LinkedList<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/collections/mod.rs b/crates/arbitrary/src/foreign/alloc/collections/mod.rs
new file mode 100644
index 0000000..67ca908
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/mod.rs
@@ -0,0 +1,5 @@
+mod binary_heap;
+mod btree_map;
+mod btree_set;
+mod linked_list;
+mod vec_deque;
diff --git a/crates/arbitrary/src/foreign/alloc/collections/vec_deque.rs b/crates/arbitrary/src/foreign/alloc/collections/vec_deque.rs
new file mode 100644
index 0000000..40e0413
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/collections/vec_deque.rs
@@ -0,0 +1,22 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::collections::vec_deque::VecDeque,
+};
+
+impl<'a, A> Arbitrary<'a> for VecDeque<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/ffi/c_str.rs b/crates/arbitrary/src/foreign/alloc/ffi/c_str.rs
new file mode 100644
index 0000000..a1b2383
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/ffi/c_str.rs
@@ -0,0 +1,19 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::ffi::CString,
+};
+
+impl<'a> Arbitrary<'a> for CString {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <Vec<u8> as Arbitrary>::arbitrary(u).map(|mut x| {
+ x.retain(|&c| c != 0);
+ // SAFETY: all zero bytes have been removed
+ unsafe { Self::from_vec_unchecked(x) }
+ })
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <Vec<u8> as Arbitrary>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/ffi/mod.rs b/crates/arbitrary/src/foreign/alloc/ffi/mod.rs
new file mode 100644
index 0000000..9238cfc
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/ffi/mod.rs
@@ -0,0 +1 @@
+mod c_str;
diff --git a/crates/arbitrary/src/foreign/alloc/mod.rs b/crates/arbitrary/src/foreign/alloc/mod.rs
new file mode 100644
index 0000000..a04b62c
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/mod.rs
@@ -0,0 +1,13 @@
+//! Implementations of [`Arbitrary`] for [`alloc`] types,
+//! excluding those in [`core`].
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod borrow;
+mod boxed;
+mod collections;
+mod ffi;
+mod rc;
+mod string;
+mod sync;
+mod vec;
diff --git a/crates/arbitrary/src/foreign/alloc/rc.rs b/crates/arbitrary/src/foreign/alloc/rc.rs
new file mode 100644
index 0000000..6d58167
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/rc.rs
@@ -0,0 +1,52 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ std::rc::Rc,
+};
+
+impl<'a, A> Arbitrary<'a> for Rc<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+ size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
+ }
+}
+
+impl<'a, A> Arbitrary<'a> for Rc<[A]>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
+
+impl<'a> Arbitrary<'a> for Rc<str> {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <&str as Arbitrary>::arbitrary(u).map(Into::into)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <&str as Arbitrary>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/string.rs b/crates/arbitrary/src/foreign/alloc/string.rs
new file mode 100644
index 0000000..a579784
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/string.rs
@@ -0,0 +1,19 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::string::String,
+};
+
+impl<'a> Arbitrary<'a> for String {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <&str as Arbitrary>::arbitrary(u).map(Into::into)
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ <&str as Arbitrary>::arbitrary_take_rest(u).map(Into::into)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <&str as Arbitrary>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/sync.rs b/crates/arbitrary/src/foreign/alloc/sync.rs
new file mode 100644
index 0000000..c8ca1db
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/sync.rs
@@ -0,0 +1,52 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ std::sync::Arc,
+};
+
+impl<'a, A> Arbitrary<'a> for Arc<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+ size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
+ }
+}
+
+impl<'a, A> Arbitrary<'a> for Arc<[A]>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
+
+impl<'a> Arbitrary<'a> for Arc<str> {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <&str as Arbitrary>::arbitrary(u).map(Into::into)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <&str as Arbitrary>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/alloc/vec.rs b/crates/arbitrary/src/foreign/alloc/vec.rs
new file mode 100644
index 0000000..63313ba
--- /dev/null
+++ b/crates/arbitrary/src/foreign/alloc/vec.rs
@@ -0,0 +1,22 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::vec::Vec,
+};
+
+impl<'a, A> Arbitrary<'a> for Vec<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/array.rs b/crates/arbitrary/src/foreign/core/array.rs
new file mode 100644
index 0000000..39075be
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/array.rs
@@ -0,0 +1,76 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ core::{
+ array,
+ mem::{self, MaybeUninit},
+ ptr,
+ },
+};
+
+/// Helper to safely create arrays since the standard library doesn't
+/// provide one yet. Shouldn't be necessary in the future.
+struct ArrayGuard<T, const N: usize> {
+ dst: *mut T,
+ initialized: usize,
+}
+
+impl<T, const N: usize> Drop for ArrayGuard<T, N> {
+ fn drop(&mut self) {
+ debug_assert!(self.initialized <= N);
+ let initialized_part = ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
+ unsafe {
+ ptr::drop_in_place(initialized_part);
+ }
+ }
+}
+
+fn try_create_array<F, T, const N: usize>(mut cb: F) -> Result<[T; N]>
+where
+ F: FnMut(usize) -> Result<T>,
+{
+ let mut array: MaybeUninit<[T; N]> = MaybeUninit::uninit();
+ let array_ptr = array.as_mut_ptr();
+ let dst = array_ptr as _;
+ let mut guard: ArrayGuard<T, N> = ArrayGuard {
+ dst,
+ initialized: 0,
+ };
+ unsafe {
+ for (idx, value_ptr) in (*array.as_mut_ptr()).iter_mut().enumerate() {
+ ptr::write(value_ptr, cb(idx)?);
+ guard.initialized += 1;
+ }
+ mem::forget(guard);
+ Ok(array.assume_init())
+ }
+}
+
+impl<'a, T, const N: usize> Arbitrary<'a> for [T; N]
+where
+ T: Arbitrary<'a>,
+{
+ #[inline]
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ try_create_array(|_| <T as Arbitrary<'a>>::arbitrary(u))
+ }
+
+ #[inline]
+ fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
+ let mut array = Self::arbitrary(&mut u)?;
+ if let Some(last) = array.last_mut() {
+ *last = Arbitrary::arbitrary_take_rest(u)?;
+ }
+ Ok(array)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+ let hint = <T as Arbitrary>::try_size_hint(depth)?;
+ Ok(size_hint::and_all(&array::from_fn::<_, N, _>(|_| hint)))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/bool.rs b/crates/arbitrary/src/foreign/core/bool.rs
new file mode 100644
index 0000000..42e82fb
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/bool.rs
@@ -0,0 +1,12 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for bool {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(<u8 as Arbitrary<'a>>::arbitrary(u)? & 1 == 1)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <u8 as Arbitrary<'a>>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/cell.rs b/crates/arbitrary/src/foreign/core/cell.rs
new file mode 100644
index 0000000..ad0db38
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/cell.rs
@@ -0,0 +1,61 @@
+use {
+ crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
+ core::cell::{Cell, RefCell, UnsafeCell},
+};
+
+impl<'a, A> Arbitrary<'a> for Cell<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ <A as Arbitrary<'a>>::try_size_hint(depth)
+ }
+}
+
+impl<'a, A> Arbitrary<'a> for RefCell<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ <A as Arbitrary<'a>>::try_size_hint(depth)
+ }
+}
+
+impl<'a, A> Arbitrary<'a> for UnsafeCell<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ <A as Arbitrary<'a>>::try_size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/char.rs b/crates/arbitrary/src/foreign/core/char.rs
new file mode 100644
index 0000000..1adc62d
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/char.rs
@@ -0,0 +1,24 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for char {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ // The highest unicode code point is 0x11_FFFF
+ const CHAR_END: u32 = 0x11_0000;
+ // The size of the surrogate blocks
+ const SURROGATES_START: u32 = 0xD800;
+ let mut c = <u32 as Arbitrary<'a>>::arbitrary(u)? % CHAR_END;
+ if let Some(c) = char::from_u32(c) {
+ Ok(c)
+ } else {
+ // We found a surrogate, wrap and try again
+ c -= SURROGATES_START;
+ Ok(char::from_u32(c)
+ .expect("Generated character should be valid! This is a bug in arbitrary-rs"))
+ }
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <u32 as Arbitrary<'a>>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/iter.rs b/crates/arbitrary/src/foreign/core/iter.rs
new file mode 100644
index 0000000..8a56f2a
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/iter.rs
@@ -0,0 +1,18 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ core::iter::{empty, Empty},
+};
+
+impl<'a, A> Arbitrary<'a> for Empty<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(empty())
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, Some(0))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/marker.rs b/crates/arbitrary/src/foreign/core/marker.rs
new file mode 100644
index 0000000..aa0afbe
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/marker.rs
@@ -0,0 +1,29 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ core::marker::{PhantomData, PhantomPinned},
+};
+
+impl<'a, A> Arbitrary<'a> for PhantomData<A>
+where
+ A: ?Sized,
+{
+ fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(PhantomData)
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, Some(0))
+ }
+}
+
+impl<'a> Arbitrary<'a> for PhantomPinned {
+ fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(PhantomPinned)
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, Some(0))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/mod.rs b/crates/arbitrary/src/foreign/core/mod.rs
new file mode 100644
index 0000000..1e525f3
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/mod.rs
@@ -0,0 +1,20 @@
+//! Implementations of [`Arbitrary`] for [`core`] types.
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod array;
+mod bool;
+mod cell;
+mod char;
+mod iter;
+mod marker;
+mod num;
+mod ops;
+mod option;
+mod result;
+mod slice;
+mod str;
+mod sync;
+mod time;
+mod tuple;
+mod unit;
diff --git a/crates/arbitrary/src/foreign/core/num.rs b/crates/arbitrary/src/foreign/core/num.rs
new file mode 100644
index 0000000..f0d5b33
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/num.rs
@@ -0,0 +1,141 @@
+use {
+ crate::{Arbitrary, Error, MaxRecursionReached, Result, Unstructured},
+ core::{
+ mem,
+ num::{
+ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
+ NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
+ },
+ },
+};
+
+macro_rules! impl_arbitrary_for_integers {
+ ( $( $ty:ty; )* ) => {
+ $(
+ impl<'a> Arbitrary<'a> for $ty {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ let mut buf = [0; mem::size_of::<$ty>()];
+ u.fill_buffer(&mut buf)?;
+ Ok(Self::from_le_bytes(buf))
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ let n = mem::size_of::<$ty>();
+ (n, Some(n))
+ }
+
+ }
+ )*
+ }
+}
+
+impl_arbitrary_for_integers! {
+ u8;
+ u16;
+ u32;
+ u64;
+ u128;
+ i8;
+ i16;
+ i32;
+ i64;
+ i128;
+}
+
+// Note: We forward Arbitrary for i/usize to i/u64 in order to simplify corpus
+// compatibility between 32-bit and 64-bit builds. This introduces dead space in
+// 32-bit builds but keeps the input layout independent of the build platform.
+impl<'a> Arbitrary<'a> for usize {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary::<u64>().map(|x| x as usize)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <u64 as Arbitrary>::size_hint(depth)
+ }
+}
+
+impl<'a> Arbitrary<'a> for isize {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary::<i64>().map(|x| x as isize)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <i64 as Arbitrary>::size_hint(depth)
+ }
+}
+
+macro_rules! impl_arbitrary_for_floats {
+ ( $( $ty:ident : $unsigned:ty; )* ) => {
+ $(
+ impl<'a> Arbitrary<'a> for $ty {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(Self::from_bits(<$unsigned as Arbitrary<'a>>::arbitrary(u)?))
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <$unsigned as Arbitrary<'a>>::size_hint(depth)
+ }
+ }
+ )*
+ }
+}
+
+impl_arbitrary_for_floats! {
+ f32: u32;
+ f64: u64;
+}
+
+macro_rules! implement_nonzero_int {
+ ($nonzero:ty, $int:ty) => {
+ impl<'a> Arbitrary<'a> for $nonzero {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ match Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?) {
+ Some(n) => Ok(n),
+ None => Err(Error::IncorrectFormat),
+ }
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <$int as Arbitrary<'a>>::size_hint(depth)
+ }
+ }
+ };
+}
+
+implement_nonzero_int! { NonZeroI8, i8 }
+implement_nonzero_int! { NonZeroI16, i16 }
+implement_nonzero_int! { NonZeroI32, i32 }
+implement_nonzero_int! { NonZeroI64, i64 }
+implement_nonzero_int! { NonZeroI128, i128 }
+implement_nonzero_int! { NonZeroIsize, isize }
+implement_nonzero_int! { NonZeroU8, u8 }
+implement_nonzero_int! { NonZeroU16, u16 }
+implement_nonzero_int! { NonZeroU32, u32 }
+implement_nonzero_int! { NonZeroU64, u64 }
+implement_nonzero_int! { NonZeroU128, u128 }
+implement_nonzero_int! { NonZeroUsize, usize }
+
+impl<'a, A> Arbitrary<'a> for Wrapping<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Wrapping)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ <A as Arbitrary<'a>>::try_size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/ops.rs b/crates/arbitrary/src/foreign/core/ops.rs
new file mode 100644
index 0000000..8c24735
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/ops.rs
@@ -0,0 +1,127 @@
+use {
+ crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured},
+ core::{
+ mem,
+ ops::{Bound, Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive},
+ },
+};
+
+macro_rules! impl_range {
+ (
+ $range:ty,
+ $value_closure:expr,
+ $value_ty:ty,
+ $fun:ident($fun_closure:expr),
+ $size_hint_closure:expr
+ ) => {
+ impl<'a, A> Arbitrary<'a> for $range
+ where
+ A: Arbitrary<'a> + Clone + PartialOrd,
+ {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ let value: $value_ty = Arbitrary::arbitrary(u)?;
+ Ok($fun(value, $fun_closure))
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ #[allow(clippy::redundant_closure_call)]
+ $size_hint_closure(depth)
+ }
+ }
+ };
+}
+impl_range!(
+ Range<A>,
+ |r: &Range<A>| (r.start.clone(), r.end.clone()),
+ (A, A),
+ bounded_range(|(a, b)| a..b),
+ |depth| Ok(crate::size_hint::and(
+ <A as Arbitrary>::try_size_hint(depth)?,
+ <A as Arbitrary>::try_size_hint(depth)?,
+ ))
+);
+impl_range!(
+ RangeFrom<A>,
+ |r: &RangeFrom<A>| r.start.clone(),
+ A,
+ unbounded_range(|a| a..),
+ |depth| <A as Arbitrary>::try_size_hint(depth)
+);
+impl_range!(
+ RangeInclusive<A>,
+ |r: &RangeInclusive<A>| (r.start().clone(), r.end().clone()),
+ (A, A),
+ bounded_range(|(a, b)| a..=b),
+ |depth| Ok(crate::size_hint::and(
+ <A as Arbitrary>::try_size_hint(depth)?,
+ <A as Arbitrary>::try_size_hint(depth)?,
+ ))
+);
+impl_range!(
+ RangeTo<A>,
+ |r: &RangeTo<A>| r.end.clone(),
+ A,
+ unbounded_range(|b| ..b),
+ |depth| <A as Arbitrary>::try_size_hint(depth)
+);
+impl_range!(
+ RangeToInclusive<A>,
+ |r: &RangeToInclusive<A>| r.end.clone(),
+ A,
+ unbounded_range(|b| ..=b),
+ |depth| <A as Arbitrary>::try_size_hint(depth)
+);
+
+pub(crate) fn bounded_range<CB, I, R>(bounds: (I, I), cb: CB) -> R
+where
+ CB: Fn((I, I)) -> R,
+ I: PartialOrd,
+ R: RangeBounds<I>,
+{
+ let (mut start, mut end) = bounds;
+ if start > end {
+ mem::swap(&mut start, &mut end);
+ }
+ cb((start, end))
+}
+
+pub(crate) fn unbounded_range<CB, I, R>(bound: I, cb: CB) -> R
+where
+ CB: Fn(I) -> R,
+ R: RangeBounds<I>,
+{
+ cb(bound)
+}
+
+impl<'a, A> Arbitrary<'a> for Bound<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ match u.int_in_range::<u8>(0..=2)? {
+ 0 => Ok(Bound::Included(A::arbitrary(u)?)),
+ 1 => Ok(Bound::Excluded(A::arbitrary(u)?)),
+ 2 => Ok(Bound::Unbounded),
+ _ => unreachable!(),
+ }
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ Ok(size_hint::or(
+ size_hint::and((1, Some(1)), A::try_size_hint(depth)?),
+ (1, Some(1)),
+ ))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/option.rs b/crates/arbitrary/src/foreign/core/option.rs
new file mode 100644
index 0000000..76ab978
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/option.rs
@@ -0,0 +1,27 @@
+use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
+
+impl<'a, A> Arbitrary<'a> for Option<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
+ Some(Arbitrary::arbitrary(u)?)
+ } else {
+ None
+ })
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ Ok(size_hint::and(
+ <bool as Arbitrary>::try_size_hint(depth)?,
+ size_hint::or((0, Some(0)), <A as Arbitrary>::try_size_hint(depth)?),
+ ))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/result.rs b/crates/arbitrary/src/foreign/core/result.rs
new file mode 100644
index 0000000..65ad50a
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/result.rs
@@ -0,0 +1,31 @@
+use crate::{size_hint, Arbitrary, Error, MaxRecursionReached, Unstructured};
+
+impl<'a, T, E> Arbitrary<'a> for Result<T, E>
+where
+ T: Arbitrary<'a>,
+ E: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, Error> {
+ Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
+ Ok(<T as Arbitrary>::arbitrary(u)?)
+ } else {
+ Err(<E as Arbitrary>::arbitrary(u)?)
+ })
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ Ok(size_hint::and(
+ <bool as Arbitrary>::size_hint(depth),
+ size_hint::or(
+ <T as Arbitrary>::try_size_hint(depth)?,
+ <E as Arbitrary>::try_size_hint(depth)?,
+ ),
+ ))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/slice.rs b/crates/arbitrary/src/foreign/core/slice.rs
new file mode 100644
index 0000000..296d474
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/slice.rs
@@ -0,0 +1,17 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for &'a [u8] {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ let len = u.arbitrary_len::<u8>()?;
+ u.bytes(len)
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ Ok(u.take_rest())
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/str.rs b/crates/arbitrary/src/foreign/core/str.rs
new file mode 100644
index 0000000..cfca8ad
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/str.rs
@@ -0,0 +1,39 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ core::str,
+};
+
+fn arbitrary_str<'a>(u: &mut Unstructured<'a>, size: usize) -> Result<&'a str> {
+ match str::from_utf8(u.peek_bytes(size).unwrap()) {
+ Ok(s) => {
+ u.bytes(size).unwrap();
+ Ok(s)
+ }
+ Err(e) => {
+ let i = e.valid_up_to();
+ let valid = u.bytes(i).unwrap();
+ let s = unsafe {
+ debug_assert!(str::from_utf8(valid).is_ok());
+ str::from_utf8_unchecked(valid)
+ };
+ Ok(s)
+ }
+ }
+}
+
+impl<'a> Arbitrary<'a> for &'a str {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ let size = u.arbitrary_len::<u8>()?;
+ arbitrary_str(u, size)
+ }
+
+ fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
+ let size = u.len();
+ arbitrary_str(&mut u, size)
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/sync/atomic.rs b/crates/arbitrary/src/foreign/core/sync/atomic.rs
new file mode 100644
index 0000000..004c396
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/sync/atomic.rs
@@ -0,0 +1,37 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ core::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize},
+};
+
+impl<'a> Arbitrary<'a> for AtomicBool {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <bool as Arbitrary<'a>>::size_hint(depth)
+ }
+}
+
+impl<'a> Arbitrary<'a> for AtomicIsize {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <isize as Arbitrary<'a>>::size_hint(depth)
+ }
+}
+
+impl<'a> Arbitrary<'a> for AtomicUsize {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <usize as Arbitrary<'a>>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/sync/mod.rs b/crates/arbitrary/src/foreign/core/sync/mod.rs
new file mode 100644
index 0000000..f7b1416
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/sync/mod.rs
@@ -0,0 +1 @@
+mod atomic;
diff --git a/crates/arbitrary/src/foreign/core/time.rs b/crates/arbitrary/src/foreign/core/time.rs
new file mode 100644
index 0000000..9ab3434
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/time.rs
@@ -0,0 +1,21 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ core::time::Duration,
+};
+
+impl<'a> Arbitrary<'a> for Duration {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(Self::new(
+ <u64 as Arbitrary>::arbitrary(u)?,
+ u.int_in_range(0..=999_999_999)?,
+ ))
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ size_hint::and(
+ <u64 as Arbitrary>::size_hint(depth),
+ <u32 as Arbitrary>::size_hint(depth),
+ )
+ }
+}
diff --git a/crates/arbitrary/src/foreign/core/tuple.rs b/crates/arbitrary/src/foreign/core/tuple.rs
new file mode 100644
index 0000000..dfdd469
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/tuple.rs
@@ -0,0 +1,38 @@
+use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
+
+macro_rules! arbitrary_tuple {
+ () => {};
+ ($last: ident $($xs: ident)*) => {
+ arbitrary_tuple!($($xs)*);
+
+ impl<'a, $($xs,)* $last> Arbitrary<'a> for ($($xs,)* $last,)
+ where
+ $($xs: Arbitrary<'a>,)*
+ $last: Arbitrary<'a>,
+ {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(($($xs::arbitrary(u)?,)* Arbitrary::arbitrary(u)?,))
+ }
+
+ #[allow(unused_mut, non_snake_case)]
+ fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
+ $(let $xs = $xs::arbitrary(&mut u)?;)*
+ let $last = $last::arbitrary_take_rest(u)?;
+ Ok(($($xs,)* $last,))
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ Ok(size_hint::and_all(&[
+ <$last as Arbitrary>::try_size_hint(depth)?,
+ $( <$xs as Arbitrary>::try_size_hint(depth)?),*
+ ]))
+ }
+ }
+ };
+}
+arbitrary_tuple!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
diff --git a/crates/arbitrary/src/foreign/core/unit.rs b/crates/arbitrary/src/foreign/core/unit.rs
new file mode 100644
index 0000000..20523a6
--- /dev/null
+++ b/crates/arbitrary/src/foreign/core/unit.rs
@@ -0,0 +1,12 @@
+use crate::{Arbitrary, Result, Unstructured};
+
+impl<'a> Arbitrary<'a> for () {
+ fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(())
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, Some(0))
+ }
+}
diff --git a/crates/arbitrary/src/foreign/mod.rs b/crates/arbitrary/src/foreign/mod.rs
new file mode 100644
index 0000000..b1c42be
--- /dev/null
+++ b/crates/arbitrary/src/foreign/mod.rs
@@ -0,0 +1,7 @@
+//! Implementations of [`Arbitrary`] for foreign types.
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod alloc;
+mod core;
+mod std;
diff --git a/crates/arbitrary/src/foreign/std/collections/hash_map.rs b/crates/arbitrary/src/foreign/std/collections/hash_map.rs
new file mode 100644
index 0000000..d2e77af
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/collections/hash_map.rs
@@ -0,0 +1,27 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::{
+ collections::hash_map::HashMap,
+ hash::{BuildHasher, Hash},
+ },
+};
+
+impl<'a, K, V, S> Arbitrary<'a> for HashMap<K, V, S>
+where
+ K: Arbitrary<'a> + Eq + Hash,
+ V: Arbitrary<'a>,
+ S: BuildHasher + Default,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/std/collections/hash_set.rs b/crates/arbitrary/src/foreign/std/collections/hash_set.rs
new file mode 100644
index 0000000..5cb63d2
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/collections/hash_set.rs
@@ -0,0 +1,26 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::{
+ collections::hash_set::HashSet,
+ hash::{BuildHasher, Hash},
+ },
+};
+
+impl<'a, A, S> Arbitrary<'a> for HashSet<A, S>
+where
+ A: Arbitrary<'a> + Eq + Hash,
+ S: BuildHasher + Default,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_iter()?.collect()
+ }
+
+ fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
+ u.arbitrary_take_rest_iter()?.collect()
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/std/collections/mod.rs b/crates/arbitrary/src/foreign/std/collections/mod.rs
new file mode 100644
index 0000000..2bde6a0
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/collections/mod.rs
@@ -0,0 +1,2 @@
+mod hash_map;
+mod hash_set;
diff --git a/crates/arbitrary/src/foreign/std/ffi/c_str.rs b/crates/arbitrary/src/foreign/std/ffi/c_str.rs
new file mode 100644
index 0000000..7a1dc29
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/ffi/c_str.rs
@@ -0,0 +1,5 @@
+// impl Arbitrary for Box<CStr> {
+// fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
+// <CString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_c_str())
+// }
+// }
diff --git a/crates/arbitrary/src/foreign/std/ffi/mod.rs b/crates/arbitrary/src/foreign/std/ffi/mod.rs
new file mode 100644
index 0000000..f5d739f
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/ffi/mod.rs
@@ -0,0 +1,2 @@
+mod c_str;
+mod os_str;
diff --git a/crates/arbitrary/src/foreign/std/ffi/os_str.rs b/crates/arbitrary/src/foreign/std/ffi/os_str.rs
new file mode 100644
index 0000000..9fa0cb3
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/ffi/os_str.rs
@@ -0,0 +1,22 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::ffi::OsString,
+};
+
+impl<'a> Arbitrary<'a> for OsString {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <String as Arbitrary>::arbitrary(u).map(From::from)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <String as Arbitrary>::size_hint(depth)
+ }
+}
+
+// impl Arbitrary for Box<OsStr> {
+// fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
+// <OsString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_osstr())
+//
+// }
+// }
diff --git a/crates/arbitrary/src/foreign/std/mod.rs b/crates/arbitrary/src/foreign/std/mod.rs
new file mode 100644
index 0000000..bc85aa2
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/mod.rs
@@ -0,0 +1,10 @@
+//! Implementations of [`Arbitrary`] for [`std`] types,
+//! excluding those in [`core`] and [`alloc`].
+//!
+//! [`Arbitrary`]: crate::Arbitrary
+
+mod collections;
+mod ffi;
+mod net;
+mod path;
+mod sync;
diff --git a/crates/arbitrary/src/foreign/std/net.rs b/crates/arbitrary/src/foreign/std/net.rs
new file mode 100644
index 0000000..41e1f8a
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/net.rs
@@ -0,0 +1,96 @@
+use {
+ crate::{size_hint, Arbitrary, Result, Unstructured},
+ std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
+};
+
+impl<'a> Arbitrary<'a> for Ipv4Addr {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(Ipv4Addr::from(u32::arbitrary(u)?))
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (4, Some(4))
+ }
+}
+
+impl<'a> Arbitrary<'a> for Ipv6Addr {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(Ipv6Addr::from(u128::arbitrary(u)?))
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (16, Some(16))
+ }
+}
+
+impl<'a> Arbitrary<'a> for IpAddr {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ if u.arbitrary()? {
+ Ok(IpAddr::V4(u.arbitrary()?))
+ } else {
+ Ok(IpAddr::V6(u.arbitrary()?))
+ }
+ }
+
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ size_hint::and(
+ bool::size_hint(depth),
+ size_hint::or(Ipv4Addr::size_hint(depth), Ipv6Addr::size_hint(depth)),
+ )
+ }
+}
+
+impl<'a> Arbitrary<'a> for SocketAddrV4 {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(SocketAddrV4::new(u.arbitrary()?, u.arbitrary()?))
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ size_hint::and(Ipv4Addr::size_hint(depth), u16::size_hint(depth))
+ }
+}
+
+impl<'a> Arbitrary<'a> for SocketAddrV6 {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(SocketAddrV6::new(
+ u.arbitrary()?,
+ u.arbitrary()?,
+ u.arbitrary()?,
+ u.arbitrary()?,
+ ))
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ size_hint::and(
+ Ipv6Addr::size_hint(depth),
+ size_hint::and(
+ u16::size_hint(depth),
+ size_hint::and(u32::size_hint(depth), u32::size_hint(depth)),
+ ),
+ )
+ }
+}
+
+impl<'a> Arbitrary<'a> for SocketAddr {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ if u.arbitrary()? {
+ Ok(SocketAddr::V4(u.arbitrary()?))
+ } else {
+ Ok(SocketAddr::V6(u.arbitrary()?))
+ }
+ }
+
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ size_hint::and(
+ bool::size_hint(depth),
+ size_hint::or(
+ SocketAddrV4::size_hint(depth),
+ SocketAddrV6::size_hint(depth),
+ ),
+ )
+ }
+}
diff --git a/crates/arbitrary/src/foreign/std/path.rs b/crates/arbitrary/src/foreign/std/path.rs
new file mode 100644
index 0000000..c94797b
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/path.rs
@@ -0,0 +1,15 @@
+use {
+ crate::{Arbitrary, Result, Unstructured},
+ std::{ffi::OsString, path::PathBuf},
+};
+
+impl<'a> Arbitrary<'a> for PathBuf {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <OsString as Arbitrary>::arbitrary(u).map(From::from)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <OsString as Arbitrary>::size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/foreign/std/sync.rs b/crates/arbitrary/src/foreign/std/sync.rs
new file mode 100644
index 0000000..312b37e
--- /dev/null
+++ b/crates/arbitrary/src/foreign/std/sync.rs
@@ -0,0 +1,23 @@
+use {
+ crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
+ std::sync::Mutex,
+};
+
+impl<'a, A> Arbitrary<'a> for Mutex<A>
+where
+ A: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Arbitrary::arbitrary(u).map(Self::new)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ A::try_size_hint(depth)
+ }
+}
diff --git a/crates/arbitrary/src/lib.rs b/crates/arbitrary/src/lib.rs
index a3fa48b..2535d3a 100644
--- a/crates/arbitrary/src/lib.rs
+++ b/crates/arbitrary/src/lib.rs
@@ -8,10 +8,10 @@
//! The `Arbitrary` trait crate.
//!
-//! This trait provides an [`Arbitrary`](./trait.Arbitrary.html) trait to
+//! This trait provides an [`Arbitrary`] trait to
//! produce well-typed, structured values, from raw, byte buffers. It is
//! generally intended to be used with fuzzers like AFL or libFuzzer. See the
-//! [`Arbitrary`](./trait.Arbitrary.html) trait's documentation for details on
+//! [`Arbitrary`] trait's documentation for details on
//! automatically deriving, implementing, and/or using the trait.
#![deny(bad_style)]
@@ -22,37 +22,34 @@
#![deny(rust_2018_idioms)]
#![deny(unused)]
+mod error;
+mod foreign;
+pub mod size_hint;
+pub mod unstructured;
+
+#[cfg(test)]
+mod tests;
+
+pub use error::*;
+
#[cfg(feature = "derive_arbitrary")]
pub use derive_arbitrary::*;
-mod error;
-pub use error::*;
-
-pub mod unstructured;
#[doc(inline)]
pub use unstructured::Unstructured;
-pub mod size_hint;
+/// Error indicating that the maximum recursion depth has been reached while calculating [`Arbitrary::size_hint`]()
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct MaxRecursionReached {}
-use core::array;
-use core::cell::{Cell, RefCell, UnsafeCell};
-use core::iter;
-use core::mem;
-use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize};
-use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
-use core::ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
-use core::str;
-use core::time::Duration;
-use std::borrow::{Cow, ToOwned};
-use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
-use std::ffi::{CString, OsString};
-use std::hash::BuildHasher;
-use std::net::{Ipv4Addr, Ipv6Addr};
-use std::ops::Bound;
-use std::path::PathBuf;
-use std::rc::Rc;
-use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize};
-use std::sync::{Arc, Mutex};
+impl core::fmt::Display for MaxRecursionReached {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.write_str("Maximum recursion depth has been reached")
+ }
+}
+
+impl std::error::Error for MaxRecursionReached {}
/// Generate arbitrary structured values from raw, unstructured data.
///
@@ -96,13 +93,32 @@
///
/// Every member of the `struct` or `enum` must also implement `Arbitrary`.
///
+/// It is also possible to change the default bounds added by the derive:
+///
+/// ```
+/// # #[cfg(feature = "derive")] mod foo {
+/// use arbitrary::Arbitrary;
+///
+/// trait Trait {
+/// type Assoc: for<'a> Arbitrary<'a>;
+/// }
+///
+/// #[derive(Arbitrary)]
+/// // The bounds are used verbatim, so any existing trait bounds will need to be repeated.
+/// #[arbitrary(bound = "T: Trait")]
+/// struct Point<T: Trait> {
+/// x: T::Assoc,
+/// }
+/// # }
+/// ```
+///
/// # Implementing `Arbitrary` By Hand
///
/// Implementing `Arbitrary` mostly involves nested calls to other `Arbitrary`
/// arbitrary implementations for each of your `struct` or `enum`'s members. But
/// sometimes you need some amount of raw data, or you need to generate a
/// variably-sized collection type, or something of that sort. The
-/// [`Unstructured`][crate::Unstructured] type helps you with these tasks.
+/// [`Unstructured`] type helps you with these tasks.
///
/// ```
/// # #[cfg(feature = "derive")] mod foo {
@@ -133,6 +149,17 @@
/// }
/// # }
/// ```
+///
+/// # A Note On Output Distributions
+///
+/// There is no requirement for a particular distribution of the values. For
+/// example, it is not required that every value appears with the same
+/// probability. That being said, the main use for `Arbitrary` is for fuzzing,
+/// so in many cases a uniform distribution will make the most sense in order to
+/// provide the best coverage of the domain. In other cases this is not
+/// desirable or even possible, for example when sampling from a uniform
+/// distribution is computationally expensive or in the case of collections that
+/// may grow indefinitely.
pub trait Arbitrary<'a>: Sized {
/// Generate an arbitrary value of `Self` from the given unstructured data.
///
@@ -176,7 +203,7 @@
/// # }
/// ```
///
- /// See also the documentation for [`Unstructured`][crate::Unstructured].
+ /// See also the documentation for [`Unstructured`].
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self>;
/// Generate an arbitrary value of `Self` from the entirety of the given
@@ -185,7 +212,7 @@
/// This is similar to Arbitrary::arbitrary, however it assumes that it is
/// the last consumer of the given data, and is thus able to consume it all
/// if it needs. See also the documentation for
- /// [`Unstructured`][crate::Unstructured].
+ /// [`Unstructured`].
fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
Self::arbitrary(&mut u)
}
@@ -196,18 +223,85 @@
/// This is useful for determining how many elements we should insert when
/// creating an arbitrary collection.
///
- /// The return value is similar to
- /// [`Iterator::size_hint`][iterator-size-hint]: it returns a tuple where
- /// the first element is a lower bound on the number of bytes required, and
- /// the second element is an optional upper bound.
+ /// The return value is similar to [`Iterator::size_hint`]: it returns a
+ /// tuple where the first element is a lower bound on the number of bytes
+ /// required, and the second element is an optional upper bound.
///
/// The default implementation return `(0, None)` which is correct for any
/// type, but not ultimately that useful. Using `#[derive(Arbitrary)]` will
/// create a better implementation. If you are writing an `Arbitrary`
/// implementation by hand, and your type can be part of a dynamically sized
/// collection (such as `Vec`), you are strongly encouraged to override this
- /// default with a better implementation. The
- /// [`size_hint`][crate::size_hint] module will help with this task.
+ /// default with a better implementation, and also override
+ /// [`try_size_hint`].
+ ///
+ /// ## How to implement this
+ ///
+ /// If the size hint calculation is a trivial constant and does not recurse
+ /// into any other `size_hint` call, you should implement it in `size_hint`:
+ ///
+ /// ```
+ /// use arbitrary::{size_hint, Arbitrary, Result, Unstructured};
+ ///
+ /// struct SomeStruct(u8);
+ ///
+ /// impl<'a> Arbitrary<'a> for SomeStruct {
+ /// fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ /// let buf = &mut [0];
+ /// u.fill_buffer(buf)?;
+ /// Ok(SomeStruct(buf[0]))
+ /// }
+ ///
+ /// #[inline]
+ /// fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ /// let _ = depth;
+ /// (1, Some(1))
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Otherwise, it should instead be implemented in [`try_size_hint`],
+ /// and the `size_hint` implementation should forward to it:
+ ///
+ /// ```
+ /// use arbitrary::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
+ ///
+ /// struct SomeStruct<A, B> {
+ /// a: A,
+ /// b: B,
+ /// }
+ ///
+ /// impl<'a, A: Arbitrary<'a>, B: Arbitrary<'a>> Arbitrary<'a> for SomeStruct<A, B> {
+ /// fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ /// // ...
+ /// # todo!()
+ /// }
+ ///
+ /// fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ /// // Return the value of try_size_hint
+ /// //
+ /// // If the recursion fails, return the default, always valid `(0, None)`
+ /// Self::try_size_hint(depth).unwrap_or_default()
+ /// }
+ ///
+ /// fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ /// // Protect against potential infinite recursion with
+ /// // `try_recursion_guard`.
+ /// size_hint::try_recursion_guard(depth, |depth| {
+ /// // If we aren't too deep, then `recursion_guard` calls
+ /// // this closure, which implements the natural size hint.
+ /// // Don't forget to use the new `depth` in all nested
+ /// // `try_size_hint` calls! We recommend shadowing the
+ /// // parameter, like what is done here, so that you can't
+ /// // accidentally use the wrong depth.
+ /// Ok(size_hint::and(
+ /// <A as Arbitrary>::try_size_hint(depth)?,
+ /// <B as Arbitrary>::try_size_hint(depth)?,
+ /// ))
+ /// })
+ /// }
+ /// }
+ /// ```
///
/// ## Invariant
///
@@ -220,21 +314,67 @@
/// outputs which are still possible to produce using the reduced input
/// space.
///
- /// ## The `depth` Parameter
+ /// [iterator-size-hint]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.size_hint
+ /// [`try_size_hint`]: Arbitrary::try_size_hint
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ let _ = depth;
+ (0, None)
+ }
+
+ /// Get a size hint for how many bytes out of an `Unstructured` this type
+ /// needs to construct itself.
+ ///
+ /// Unlike [`size_hint`], this function keeps the information that the
+ /// recursion limit was reached. This is required to "short circuit" the
+ /// calculation and avoid exponential blowup with recursive structures.
+ ///
+ /// If you are implementing [`size_hint`] for a struct that could be
+ /// recursive, you should implement `try_size_hint` and call the
+ /// `try_size_hint` when recursing
+ ///
+ ///
+ /// The return value is similar to [`core::iter::Iterator::size_hint`]: it
+ /// returns a tuple where the first element is a lower bound on the number
+ /// of bytes required, and the second element is an optional upper bound.
+ ///
+ /// The default implementation returns the value of [`size_hint`] which is
+ /// correct for any type, but might lead to exponential blowup when dealing
+ /// with recursive types.
+ ///
+ /// ## Invariant
+ ///
+ /// It must be possible to construct every possible output using only inputs
+ /// of lengths bounded by these parameters. This applies to both
+ /// [`Arbitrary::arbitrary`] and [`Arbitrary::arbitrary_take_rest`].
+ ///
+ /// This is trivially true for `(0, None)`. To restrict this further, it
+ /// must be proven that all inputs that are now excluded produced redundant
+ /// outputs which are still possible to produce using the reduced input
+ /// space.
+ ///
+ /// ## When to implement `try_size_hint`
///
/// If you 100% know that the type you are implementing `Arbitrary` for is
/// not a recursive type, or your implementation is not transitively calling
- /// any other `size_hint` methods, you can ignore the `depth` parameter.
+ /// any other `size_hint` methods, you may implement [`size_hint`], and the
+ /// default `try_size_hint` implementation will use it.
+ ///
/// Note that if you are implementing `Arbitrary` for a generic type, you
/// cannot guarantee the lack of type recursion!
///
- /// Otherwise, you need to use
- /// [`arbitrary::size_hint::recursion_guard(depth)`][crate::size_hint::recursion_guard]
+ /// Otherwise, when there is possible type recursion, you should implement
+ /// `try_size_hint` instead.
+ ///
+ /// ## The `depth` parameter
+ ///
+ /// When implementing `try_size_hint`, you need to use
+ /// [`arbitrary::size_hint::try_recursion_guard(depth)`][crate::size_hint::try_recursion_guard]
/// to prevent potential infinite recursion when calculating size hints for
/// potentially recursive types:
///
/// ```
- /// use arbitrary::{Arbitrary, Unstructured, size_hint};
+ /// use arbitrary::{size_hint, Arbitrary, MaxRecursionReached, Unstructured};
///
/// // This can potentially be a recursive type if `L` or `R` contain
/// // something like `Box<Option<MyEither<L, R>>>`!
@@ -254,1042 +394,34 @@
/// }
///
/// fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ /// // Return the value of `try_size_hint`
+ /// //
+ /// // If the recursion fails, return the default `(0, None)` range,
+ /// // which is always valid.
+ /// Self::try_size_hint(depth).unwrap_or_default()
+ /// }
+ ///
+ /// fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
/// // Protect against potential infinite recursion with
- /// // `recursion_guard`.
- /// size_hint::recursion_guard(depth, |depth| {
+ /// // `try_recursion_guard`.
+ /// size_hint::try_recursion_guard(depth, |depth| {
/// // If we aren't too deep, then `recursion_guard` calls
/// // this closure, which implements the natural size hint.
/// // Don't forget to use the new `depth` in all nested
- /// // `size_hint` calls! We recommend shadowing the
+ /// // `try_size_hint` calls! We recommend shadowing the
/// // parameter, like what is done here, so that you can't
/// // accidentally use the wrong depth.
- /// size_hint::or(
- /// <L as Arbitrary>::size_hint(depth),
- /// <R as Arbitrary>::size_hint(depth),
- /// )
+ /// Ok(size_hint::or(
+ /// <L as Arbitrary>::try_size_hint(depth)?,
+ /// <R as Arbitrary>::try_size_hint(depth)?,
+ /// ))
/// })
/// }
/// }
/// ```
- ///
- /// [iterator-size-hint]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.size_hint
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- let _ = depth;
- (0, None)
- }
-}
-
-impl<'a> Arbitrary<'a> for () {
- fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
- Ok(())
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, Some(0))
- }
-}
-
-impl<'a> Arbitrary<'a> for bool {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(<u8 as Arbitrary<'a>>::arbitrary(u)? & 1 == 1)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <u8 as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-macro_rules! impl_arbitrary_for_integers {
- ( $( $ty:ty: $unsigned:ty; )* ) => {
- $(
- impl<'a> Arbitrary<'a> for $ty {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- let mut buf = [0; mem::size_of::<$ty>()];
- u.fill_buffer(&mut buf)?;
- let mut x: $unsigned = 0;
- for i in 0..mem::size_of::<$ty>() {
- x |= buf[i] as $unsigned << (i * 8);
- }
- Ok(x as $ty)
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- let n = mem::size_of::<$ty>();
- (n, Some(n))
- }
-
- }
- )*
- }
-}
-
-impl_arbitrary_for_integers! {
- u8: u8;
- u16: u16;
- u32: u32;
- u64: u64;
- u128: u128;
- usize: usize;
- i8: u8;
- i16: u16;
- i32: u32;
- i64: u64;
- i128: u128;
- isize: usize;
-}
-
-macro_rules! impl_arbitrary_for_floats {
- ( $( $ty:ident : $unsigned:ty; )* ) => {
- $(
- impl<'a> Arbitrary<'a> for $ty {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(Self::from_bits(<$unsigned as Arbitrary<'a>>::arbitrary(u)?))
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <$unsigned as Arbitrary<'a>>::size_hint(depth)
- }
- }
- )*
- }
-}
-
-impl_arbitrary_for_floats! {
- f32: u32;
- f64: u64;
-}
-
-impl<'a> Arbitrary<'a> for char {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- use std::char;
- // The highest unicode code point is 0x11_FFFF
- const CHAR_END: u32 = 0x11_0000;
- // The size of the surrogate blocks
- const SURROGATES_START: u32 = 0xD800;
- let mut c = <u32 as Arbitrary<'a>>::arbitrary(u)? % CHAR_END;
- if let Some(c) = char::from_u32(c) {
- Ok(c)
- } else {
- // We found a surrogate, wrap and try again
- c -= SURROGATES_START;
- Ok(char::from_u32(c)
- .expect("Generated character should be valid! This is a bug in arbitrary-rs"))
- }
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <u32 as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for AtomicBool {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <bool as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for AtomicIsize {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <isize as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for AtomicUsize {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <usize as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-macro_rules! impl_range {
- (
- $range:ty,
- $value_closure:expr,
- $value_ty:ty,
- $fun:ident($fun_closure:expr),
- $size_hint_closure:expr
- ) => {
- impl<'a, A> Arbitrary<'a> for $range
- where
- A: Arbitrary<'a> + Clone + PartialOrd,
- {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- let value: $value_ty = Arbitrary::arbitrary(u)?;
- Ok($fun(value, $fun_closure))
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- $size_hint_closure(depth)
- }
- }
- };
-}
-
-impl_range!(
- Range<A>,
- |r: &Range<A>| (r.start.clone(), r.end.clone()),
- (A, A),
- bounded_range(|(a, b)| a..b),
- |depth| crate::size_hint::and(
- <A as Arbitrary>::size_hint(depth),
- <A as Arbitrary>::size_hint(depth)
- )
-);
-impl_range!(
- RangeFrom<A>,
- |r: &RangeFrom<A>| r.start.clone(),
- A,
- unbounded_range(|a| a..),
- |depth| <A as Arbitrary>::size_hint(depth)
-);
-impl_range!(
- RangeInclusive<A>,
- |r: &RangeInclusive<A>| (r.start().clone(), r.end().clone()),
- (A, A),
- bounded_range(|(a, b)| a..=b),
- |depth| crate::size_hint::and(
- <A as Arbitrary>::size_hint(depth),
- <A as Arbitrary>::size_hint(depth)
- )
-);
-impl_range!(
- RangeTo<A>,
- |r: &RangeTo<A>| r.end.clone(),
- A,
- unbounded_range(|b| ..b),
- |depth| <A as Arbitrary>::size_hint(depth)
-);
-impl_range!(
- RangeToInclusive<A>,
- |r: &RangeToInclusive<A>| r.end.clone(),
- A,
- unbounded_range(|b| ..=b),
- |depth| <A as Arbitrary>::size_hint(depth)
-);
-
-pub(crate) fn bounded_range<CB, I, R>(bounds: (I, I), cb: CB) -> R
-where
- CB: Fn((I, I)) -> R,
- I: PartialOrd,
- R: RangeBounds<I>,
-{
- let (mut start, mut end) = bounds;
- if start > end {
- mem::swap(&mut start, &mut end);
- }
- cb((start, end))
-}
-
-pub(crate) fn unbounded_range<CB, I, R>(bound: I, cb: CB) -> R
-where
- CB: Fn(I) -> R,
- R: RangeBounds<I>,
-{
- cb(bound)
-}
-
-impl<'a> Arbitrary<'a> for Duration {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(Self::new(
- <u64 as Arbitrary>::arbitrary(u)?,
- u.int_in_range(0..=999_999_999)?,
- ))
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(
- <u64 as Arbitrary>::size_hint(depth),
- <u32 as Arbitrary>::size_hint(depth),
- )
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Option<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
- Some(Arbitrary::arbitrary(u)?)
- } else {
- None
- })
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(
- <bool as Arbitrary>::size_hint(depth),
- crate::size_hint::or((0, Some(0)), <A as Arbitrary>::size_hint(depth)),
- )
- }
-}
-
-impl<'a, A: Arbitrary<'a>, B: Arbitrary<'a>> Arbitrary<'a> for std::result::Result<A, B> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
- Ok(<A as Arbitrary>::arbitrary(u)?)
- } else {
- Err(<B as Arbitrary>::arbitrary(u)?)
- })
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(
- <bool as Arbitrary>::size_hint(depth),
- crate::size_hint::or(
- <A as Arbitrary>::size_hint(depth),
- <B as Arbitrary>::size_hint(depth),
- ),
- )
- }
-}
-
-macro_rules! arbitrary_tuple {
- () => {};
- ($last: ident $($xs: ident)*) => {
- arbitrary_tuple!($($xs)*);
-
- impl<'a, $($xs: Arbitrary<'a>,)* $last: Arbitrary<'a>> Arbitrary<'a> for ($($xs,)* $last,) {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(($($xs::arbitrary(u)?,)* Arbitrary::arbitrary(u)?,))
- }
-
- #[allow(unused_mut, non_snake_case)]
- fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
- $(let $xs = $xs::arbitrary(&mut u)?;)*
- let $last = $last::arbitrary_take_rest(u)?;
- Ok(($($xs,)* $last,))
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and_all(&[
- <$last as Arbitrary>::size_hint(depth),
- $( <$xs as Arbitrary>::size_hint(depth) ),*
- ])
- }
- }
- };
-}
-arbitrary_tuple!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
-
-// Helper to safely create arrays since the standard library doesn't
-// provide one yet. Shouldn't be necessary in the future.
-struct ArrayGuard<T, const N: usize> {
- dst: *mut T,
- initialized: usize,
-}
-
-impl<T, const N: usize> Drop for ArrayGuard<T, N> {
- fn drop(&mut self) {
- debug_assert!(self.initialized <= N);
- let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
- unsafe {
- core::ptr::drop_in_place(initialized_part);
- }
- }
-}
-
-fn try_create_array<F, T, const N: usize>(mut cb: F) -> Result<[T; N]>
-where
- F: FnMut(usize) -> Result<T>,
-{
- let mut array: mem::MaybeUninit<[T; N]> = mem::MaybeUninit::uninit();
- let array_ptr = array.as_mut_ptr();
- let dst = array_ptr as _;
- let mut guard: ArrayGuard<T, N> = ArrayGuard {
- dst,
- initialized: 0,
- };
- unsafe {
- for (idx, value_ptr) in (*array.as_mut_ptr()).iter_mut().enumerate() {
- core::ptr::write(value_ptr, cb(idx)?);
- guard.initialized += 1;
- }
- mem::forget(guard);
- Ok(array.assume_init())
- }
-}
-
-impl<'a, T, const N: usize> Arbitrary<'a> for [T; N]
-where
- T: Arbitrary<'a>,
-{
- #[inline]
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- try_create_array(|_| <T as Arbitrary<'a>>::arbitrary(u))
- }
-
- #[inline]
- fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
- let mut array = Self::arbitrary(&mut u)?;
- if let Some(last) = array.last_mut() {
- *last = Arbitrary::arbitrary_take_rest(u)?;
- }
- Ok(array)
- }
-
- #[inline]
- fn size_hint(d: usize) -> (usize, Option<usize>) {
- crate::size_hint::and_all(&array::from_fn::<_, N, _>(|_| {
- <T as Arbitrary>::size_hint(d)
- }))
- }
-}
-
-impl<'a> Arbitrary<'a> for &'a [u8] {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- let len = u.arbitrary_len::<u8>()?;
- u.bytes(len)
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- Ok(u.take_rest())
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Vec<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, K: Arbitrary<'a> + Ord, V: Arbitrary<'a>> Arbitrary<'a> for BTreeMap<K, V> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BTreeSet<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Bound<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- match u.int_in_range::<u8>(0..=2)? {
- 0 => Ok(Bound::Included(A::arbitrary(u)?)),
- 1 => Ok(Bound::Excluded(A::arbitrary(u)?)),
- 2 => Ok(Bound::Unbounded),
- _ => unreachable!(),
- }
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- size_hint::or(
- size_hint::and((1, Some(1)), A::size_hint(depth)),
- (1, Some(1)),
- )
- }
-}
-
-impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BinaryHeap<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>, S: BuildHasher + Default>
- Arbitrary<'a> for HashMap<K, V, S>
-{
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A: Arbitrary<'a> + Eq + ::std::hash::Hash, S: BuildHasher + Default> Arbitrary<'a>
- for HashSet<A, S>
-{
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for LinkedList<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for VecDeque<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- u.arbitrary_iter()?.collect()
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- u.arbitrary_take_rest_iter()?.collect()
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a, A> Arbitrary<'a> for Cow<'a, A>
-where
- A: ToOwned + ?Sized,
- <A as ToOwned>::Owned: Arbitrary<'a>,
-{
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Cow::Owned)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, |depth| {
- <<A as ToOwned>::Owned as Arbitrary>::size_hint(depth)
- })
- }
-}
-
-impl<'a> Arbitrary<'a> for &'a str {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- let size = u.arbitrary_len::<u8>()?;
- match str::from_utf8(u.peek_bytes(size).unwrap()) {
- Ok(s) => {
- u.bytes(size).unwrap();
- Ok(s)
- }
- Err(e) => {
- let i = e.valid_up_to();
- let valid = u.bytes(i).unwrap();
- let s = unsafe {
- debug_assert!(str::from_utf8(valid).is_ok());
- str::from_utf8_unchecked(valid)
- };
- Ok(s)
- }
- }
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- let bytes = u.take_rest();
- str::from_utf8(bytes).map_err(|_| Error::IncorrectFormat)
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, None)
- }
-}
-
-impl<'a> Arbitrary<'a> for String {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <&str as Arbitrary>::arbitrary(u).map(Into::into)
- }
-
- fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
- <&str as Arbitrary>::arbitrary_take_rest(u).map(Into::into)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <&str as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for CString {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <Vec<u8> as Arbitrary>::arbitrary(u).map(|mut x| {
- x.retain(|&c| c != 0);
- Self::new(x).unwrap()
- })
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <Vec<u8> as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for OsString {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <String as Arbitrary>::arbitrary(u).map(From::from)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <String as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for PathBuf {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <OsString as Arbitrary>::arbitrary(u).map(From::from)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <OsString as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box<[A]> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <Vec<A> as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_slice())
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <Vec<A> as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a> Arbitrary<'a> for Box<str> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <String as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_str())
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <String as Arbitrary>::size_hint(depth)
- }
-}
-
-// impl Arbitrary for Box<CStr> {
-// fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
-// <CString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_c_str())
-// }
-// }
-
-// impl Arbitrary for Box<OsStr> {
-// fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
-// <OsString as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_osstr())
-//
-// }
-// }
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Arc<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
- }
-}
-
-impl<'a> Arbitrary<'a> for Arc<str> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <&str as Arbitrary>::arbitrary(u).map(Into::into)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <&str as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Rc<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
- }
-}
-
-impl<'a> Arbitrary<'a> for Rc<str> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- <&str as Arbitrary>::arbitrary(u).map(Into::into)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <&str as Arbitrary>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Cell<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <A as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for RefCell<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <A as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for UnsafeCell<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <A as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Mutex<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(Self::new)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <A as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for iter::Empty<A> {
- fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
- Ok(iter::empty())
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, Some(0))
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::marker::PhantomData<A> {
- fn arbitrary(_: &mut Unstructured<'a>) -> Result<Self> {
- Ok(::std::marker::PhantomData)
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (0, Some(0))
- }
-}
-
-impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::num::Wrapping<A> {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Arbitrary::arbitrary(u).map(::std::num::Wrapping)
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <A as Arbitrary<'a>>::size_hint(depth)
- }
-}
-
-macro_rules! implement_nonzero_int {
- ($nonzero:ty, $int:ty) => {
- impl<'a> Arbitrary<'a> for $nonzero {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- match Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?) {
- Some(n) => Ok(n),
- None => Err(Error::IncorrectFormat),
- }
- }
-
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <$int as Arbitrary<'a>>::size_hint(depth)
- }
- }
- };
-}
-
-implement_nonzero_int! { NonZeroI8, i8 }
-implement_nonzero_int! { NonZeroI16, i16 }
-implement_nonzero_int! { NonZeroI32, i32 }
-implement_nonzero_int! { NonZeroI64, i64 }
-implement_nonzero_int! { NonZeroI128, i128 }
-implement_nonzero_int! { NonZeroIsize, isize }
-implement_nonzero_int! { NonZeroU8, u8 }
-implement_nonzero_int! { NonZeroU16, u16 }
-implement_nonzero_int! { NonZeroU32, u32 }
-implement_nonzero_int! { NonZeroU64, u64 }
-implement_nonzero_int! { NonZeroU128, u128 }
-implement_nonzero_int! { NonZeroUsize, usize }
-
-impl<'a> Arbitrary<'a> for Ipv4Addr {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(Ipv4Addr::from(u32::arbitrary(u)?))
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (4, Some(4))
- }
-}
-
-impl<'a> Arbitrary<'a> for Ipv6Addr {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
- Ok(Ipv6Addr::from(u128::arbitrary(u)?))
- }
-
- #[inline]
- fn size_hint(_depth: usize) -> (usize, Option<usize>) {
- (16, Some(16))
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- /// Generates an arbitrary `T`, and checks that the result is consistent with the
- /// `size_hint()` reported by `T`.
- fn checked_arbitrary<'a, T: Arbitrary<'a>>(u: &mut Unstructured<'a>) -> Result<T> {
- let (min, max) = T::size_hint(0);
-
- let len_before = u.len();
- let result = T::arbitrary(u);
-
- let consumed = len_before - u.len();
-
- if let Some(max) = max {
- assert!(
- consumed <= max,
- "incorrect maximum size: indicated {}, actually consumed {}",
- max,
- consumed
- );
- }
-
- if result.is_ok() {
- assert!(
- consumed >= min,
- "incorrect minimum size: indicated {}, actually consumed {}",
- min,
- consumed
- );
- }
-
- result
- }
-
- /// Like `checked_arbitrary()`, but calls `arbitrary_take_rest()` instead of `arbitrary()`.
- fn checked_arbitrary_take_rest<'a, T: Arbitrary<'a>>(u: Unstructured<'a>) -> Result<T> {
- let (min, _) = T::size_hint(0);
-
- let len_before = u.len();
- let result = T::arbitrary_take_rest(u);
-
- if result.is_ok() {
- assert!(
- len_before >= min,
- "incorrect minimum size: indicated {}, worked with {}",
- min,
- len_before
- );
- }
-
- result
- }
-
- #[test]
- fn finite_buffer_fill_buffer() {
- let x = [1, 2, 3, 4];
- let mut rb = Unstructured::new(&x);
- let mut z = [0; 2];
- rb.fill_buffer(&mut z).unwrap();
- assert_eq!(z, [1, 2]);
- rb.fill_buffer(&mut z).unwrap();
- assert_eq!(z, [3, 4]);
- rb.fill_buffer(&mut z).unwrap();
- assert_eq!(z, [0, 0]);
- }
-
- #[test]
- fn arbitrary_for_integers() {
- let x = [1, 2, 3, 4];
- let mut buf = Unstructured::new(&x);
- let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24);
- let actual = checked_arbitrary::<i32>(&mut buf).unwrap();
- assert_eq!(expected, actual);
- }
-
- #[test]
- fn arbitrary_for_bytes() {
- let x = [1, 2, 3, 4, 4];
- let mut buf = Unstructured::new(&x);
- let expected = &[1, 2, 3, 4];
- let actual = checked_arbitrary::<&[u8]>(&mut buf).unwrap();
- assert_eq!(expected, actual);
- }
-
- #[test]
- fn arbitrary_take_rest_for_bytes() {
- let x = [1, 2, 3, 4];
- let buf = Unstructured::new(&x);
- let expected = &[1, 2, 3, 4];
- let actual = checked_arbitrary_take_rest::<&[u8]>(buf).unwrap();
- assert_eq!(expected, actual);
- }
-
- #[test]
- fn arbitrary_collection() {
- let x = [
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 12,
- ];
- assert_eq!(
- checked_arbitrary::<&[u8]>(&mut Unstructured::new(&x)).unwrap(),
- &[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
- );
- assert_eq!(
- checked_arbitrary::<Vec<u8>>(&mut Unstructured::new(&x)).unwrap(),
- &[2, 4, 6, 8, 1]
- );
- assert_eq!(
- checked_arbitrary::<Vec<u32>>(&mut Unstructured::new(&x)).unwrap(),
- &[84148994]
- );
- assert_eq!(
- checked_arbitrary::<String>(&mut Unstructured::new(&x)).unwrap(),
- "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03"
- );
- }
-
- #[test]
- fn arbitrary_take_rest() {
- let x = [1, 2, 3, 4];
- assert_eq!(
- checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&x)).unwrap(),
- &[1, 2, 3, 4]
- );
- assert_eq!(
- checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&x)).unwrap(),
- &[1, 2, 3, 4]
- );
- assert_eq!(
- checked_arbitrary_take_rest::<Vec<u32>>(Unstructured::new(&x)).unwrap(),
- &[0x4030201]
- );
- assert_eq!(
- checked_arbitrary_take_rest::<String>(Unstructured::new(&x)).unwrap(),
- "\x01\x02\x03\x04"
- );
-
- assert_eq!(
- checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&[])).unwrap(),
- &[]
- );
- assert_eq!(
- checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&[])).unwrap(),
- &[]
- );
- }
-
- #[test]
- fn size_hint_for_tuples() {
- assert_eq!(
- (7, Some(7)),
- <(bool, u16, i32) as Arbitrary<'_>>::size_hint(0)
- );
- assert_eq!((1, None), <(u8, Vec<u8>) as Arbitrary>::size_hint(0));
+ fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
+ Ok(Self::size_hint(depth))
}
}
@@ -1338,5 +470,65 @@
/// x: i32,
/// }
/// ```
+///
+/// Multiple conflicting bounds at the container-level:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default")]
+/// #[arbitrary(bound = "T: Default")]
+/// struct Point<T: Default> {
+/// #[arbitrary(default)]
+/// x: T,
+/// }
+/// ```
+///
+/// Multiple conflicting bounds in a single bound attribute:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default, T: Default")]
+/// struct Point<T: Default> {
+/// #[arbitrary(default)]
+/// x: T,
+/// }
+/// ```
+///
+/// Multiple conflicting bounds in multiple bound attributes:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default", bound = "T: Default")]
+/// struct Point<T: Default> {
+/// #[arbitrary(default)]
+/// x: T,
+/// }
+/// ```
+///
+/// Too many bounds supplied:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default")]
+/// struct Point {
+/// x: i32,
+/// }
+/// ```
+///
+/// Too many bounds supplied across multiple attributes:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// #[arbitrary(bound = "T: Default")]
+/// #[arbitrary(bound = "U: Default")]
+/// struct Point<T: Default> {
+/// #[arbitrary(default)]
+/// x: T,
+/// }
+/// ```
+///
+/// Attempt to use the derive attribute on an enum variant:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// enum Enum<T: Default> {
+/// #[arbitrary(default)]
+/// Variant(T),
+/// }
+/// ```
#[cfg(all(doctest, feature = "derive"))]
pub struct CompileFailTests;
diff --git a/crates/arbitrary/src/size_hint.rs b/crates/arbitrary/src/size_hint.rs
index 045c148..95707ee 100644
--- a/crates/arbitrary/src/size_hint.rs
+++ b/crates/arbitrary/src/size_hint.rs
@@ -1,6 +1,8 @@
//! Utilities for working with and combining the results of
//! [`Arbitrary::size_hint`][crate::Arbitrary::size_hint].
+pub(crate) const MAX_DEPTH: usize = 20;
+
/// Protects against potential infinite recursion when calculating size hints
/// due to indirect type recursion.
///
@@ -8,12 +10,14 @@
/// size hint.
///
/// Otherwise, returns the default size hint: `(0, None)`.
+///
+/// <div class="warning">This method is deprecated. Users should instead implement <a href="../trait.Arbitrary.html#method.try_size_hint"><code>try_size_hint</code></a> and use <a href="fn.try_recursion_guard.html"><code>try_recursion_guard</code></a></div>
#[inline]
+#[deprecated(note = "use `try_recursion_guard` instead")]
pub fn recursion_guard(
depth: usize,
f: impl FnOnce(usize) -> (usize, Option<usize>),
) -> (usize, Option<usize>) {
- const MAX_DEPTH: usize = 20;
if depth > MAX_DEPTH {
(0, None)
} else {
@@ -21,6 +25,27 @@
}
}
+/// Protects against potential infinite recursion when calculating size hints
+/// due to indirect type recursion.
+///
+/// When the depth is not too deep, calls `f` with `depth + 1` to calculate the
+/// size hint.
+///
+/// Otherwise, returns an error.
+///
+/// This should be used when implementing [`try_size_hint`](crate::Arbitrary::try_size_hint)
+#[inline]
+pub fn try_recursion_guard(
+ depth: usize,
+ f: impl FnOnce(usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached>,
+) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
+ if depth > MAX_DEPTH {
+ Err(crate::MaxRecursionReached {})
+ } else {
+ f(depth + 1)
+ }
+}
+
/// Take the sum of the `lhs` and `rhs` size hints.
#[inline]
pub fn and(lhs: (usize, Option<usize>), rhs: (usize, Option<usize>)) -> (usize, Option<usize>) {
diff --git a/crates/arbitrary/src/tests.rs b/crates/arbitrary/src/tests.rs
new file mode 100644
index 0000000..7746715
--- /dev/null
+++ b/crates/arbitrary/src/tests.rs
@@ -0,0 +1,316 @@
+use {
+ super::{Arbitrary, Result, Unstructured},
+ std::{collections::HashSet, fmt::Debug, hash::Hash, rc::Rc, sync::Arc},
+};
+
+/// Assert that the given expected values are all generated.
+///
+/// Exhaustively enumerates all buffers up to length 10 containing the
+/// following bytes: `0x00`, `0x01`, `0x61` (aka ASCII 'a'), and `0xff`
+fn assert_generates<T>(expected_values: impl IntoIterator<Item = T>)
+where
+ T: Clone + Debug + Hash + Eq + for<'a> Arbitrary<'a>,
+{
+ let expected_values: HashSet<_> = expected_values.into_iter().collect();
+ let mut arbitrary_expected = expected_values.clone();
+ let mut arbitrary_take_rest_expected = expected_values;
+
+ let bytes = [0, 1, b'a', 0xff];
+ let max_len = 10;
+
+ let mut buf = Vec::with_capacity(max_len);
+
+ let mut g = exhaustigen::Gen::new();
+ while !g.done() {
+ let len = g.gen(max_len);
+
+ buf.clear();
+ buf.extend(
+ std::iter::repeat_with(|| {
+ let index = g.gen(bytes.len() - 1);
+ bytes[index]
+ })
+ .take(len),
+ );
+
+ let mut u = Unstructured::new(&buf);
+ let val = T::arbitrary(&mut u).unwrap();
+ arbitrary_expected.remove(&val);
+
+ let u = Unstructured::new(&buf);
+ let val = T::arbitrary_take_rest(u).unwrap();
+ arbitrary_take_rest_expected.remove(&val);
+
+ if arbitrary_expected.is_empty() && arbitrary_take_rest_expected.is_empty() {
+ return;
+ }
+ }
+
+ panic!(
+ "failed to generate all expected values!\n\n\
+ T::arbitrary did not generate: {arbitrary_expected:#?}\n\n\
+ T::arbitrary_take_rest did not generate {arbitrary_take_rest_expected:#?}"
+ )
+}
+
+/// Generates an arbitrary `T`, and checks that the result is consistent with the
+/// `size_hint()` reported by `T`.
+fn checked_arbitrary<'a, T: Arbitrary<'a>>(u: &mut Unstructured<'a>) -> Result<T> {
+ let (min, max) = T::size_hint(0);
+
+ let len_before = u.len();
+ let result = T::arbitrary(u);
+
+ let consumed = len_before - u.len();
+
+ if let Some(max) = max {
+ assert!(
+ consumed <= max,
+ "incorrect maximum size: indicated {}, actually consumed {}",
+ max,
+ consumed
+ );
+ }
+
+ if result.is_ok() {
+ assert!(
+ consumed >= min,
+ "incorrect minimum size: indicated {}, actually consumed {}",
+ min,
+ consumed
+ );
+ }
+
+ result
+}
+
+/// Like `checked_arbitrary()`, but calls `arbitrary_take_rest()` instead of `arbitrary()`.
+fn checked_arbitrary_take_rest<'a, T: Arbitrary<'a>>(u: Unstructured<'a>) -> Result<T> {
+ let (min, _) = T::size_hint(0);
+
+ let len_before = u.len();
+ let result = T::arbitrary_take_rest(u);
+
+ if result.is_ok() {
+ assert!(
+ len_before >= min,
+ "incorrect minimum size: indicated {}, worked with {}",
+ min,
+ len_before
+ );
+ }
+
+ result
+}
+
+#[test]
+fn finite_buffer_fill_buffer() {
+ let x = [1, 2, 3, 4];
+ let mut rb = Unstructured::new(&x);
+ let mut z = [0; 2];
+ rb.fill_buffer(&mut z).unwrap();
+ assert_eq!(z, [1, 2]);
+ rb.fill_buffer(&mut z).unwrap();
+ assert_eq!(z, [3, 4]);
+ rb.fill_buffer(&mut z).unwrap();
+ assert_eq!(z, [0, 0]);
+}
+
+#[test]
+fn arbitrary_for_integers() {
+ let x = [1, 2, 3, 4];
+ let mut buf = Unstructured::new(&x);
+ let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24);
+ let actual = checked_arbitrary::<i32>(&mut buf).unwrap();
+ assert_eq!(expected, actual);
+
+ assert_generates([
+ i32::from_ne_bytes([0, 0, 0, 0]),
+ i32::from_ne_bytes([0, 0, 0, 1]),
+ i32::from_ne_bytes([0, 0, 1, 0]),
+ i32::from_ne_bytes([0, 1, 0, 0]),
+ i32::from_ne_bytes([1, 0, 0, 0]),
+ i32::from_ne_bytes([1, 1, 1, 1]),
+ i32::from_ne_bytes([0xff, 0xff, 0xff, 0xff]),
+ ]);
+}
+
+#[test]
+fn arbitrary_for_bytes() {
+ let x = [1, 2, 3, 4, 4];
+ let mut buf = Unstructured::new(&x);
+ let expected = &[1, 2, 3, 4];
+ let actual = checked_arbitrary::<&[u8]>(&mut buf).unwrap();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn arbitrary_take_rest_for_bytes() {
+ let x = [1, 2, 3, 4];
+ let buf = Unstructured::new(&x);
+ let expected = &[1, 2, 3, 4];
+ let actual = checked_arbitrary_take_rest::<&[u8]>(buf).unwrap();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn arbitrary_for_vec_u8() {
+ assert_generates::<Vec<u8>>([
+ vec![],
+ vec![0],
+ vec![1],
+ vec![0, 0],
+ vec![0, 1],
+ vec![1, 0],
+ vec![1, 1],
+ vec![0, 0, 0],
+ vec![0, 0, 1],
+ vec![0, 1, 0],
+ vec![0, 1, 1],
+ vec![1, 0, 0],
+ vec![1, 0, 1],
+ vec![1, 1, 0],
+ vec![1, 1, 1],
+ ]);
+}
+
+#[test]
+fn arbitrary_for_vec_vec_u8() {
+ assert_generates::<Vec<Vec<u8>>>([
+ vec![],
+ vec![vec![]],
+ vec![vec![0]],
+ vec![vec![1]],
+ vec![vec![0, 1]],
+ vec![vec![], vec![]],
+ vec![vec![0], vec![]],
+ vec![vec![], vec![1]],
+ vec![vec![0], vec![1]],
+ vec![vec![0, 1], vec![]],
+ vec![vec![], vec![1, 0]],
+ vec![vec![], vec![], vec![]],
+ ]);
+}
+
+#[test]
+fn arbitrary_for_vec_vec_vec_u8() {
+ assert_generates::<Vec<Vec<Vec<u8>>>>([
+ vec![],
+ vec![vec![]],
+ vec![vec![vec![0]]],
+ vec![vec![vec![1]]],
+ vec![vec![vec![0, 1]]],
+ vec![vec![], vec![]],
+ vec![vec![], vec![vec![]]],
+ vec![vec![vec![]], vec![]],
+ vec![vec![vec![]], vec![vec![]]],
+ vec![vec![vec![0]], vec![]],
+ vec![vec![], vec![vec![1]]],
+ vec![vec![vec![0]], vec![vec![1]]],
+ vec![vec![vec![0, 1]], vec![]],
+ vec![vec![], vec![vec![0, 1]]],
+ vec![vec![], vec![], vec![]],
+ vec![vec![vec![]], vec![], vec![]],
+ vec![vec![], vec![vec![]], vec![]],
+ vec![vec![], vec![], vec![vec![]]],
+ ]);
+}
+
+#[test]
+fn arbitrary_for_string() {
+ assert_generates::<String>(["".into(), "a".into(), "aa".into(), "aaa".into()]);
+}
+
+#[test]
+fn arbitrary_collection() {
+ let x = [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 12,
+ ];
+ assert_eq!(
+ checked_arbitrary::<&[u8]>(&mut Unstructured::new(&x)).unwrap(),
+ &[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
+ );
+ assert_eq!(
+ checked_arbitrary::<Vec<u8>>(&mut Unstructured::new(&x)).unwrap(),
+ &[2, 4, 6, 8, 1]
+ );
+ assert_eq!(
+ &*checked_arbitrary::<Box<[u8]>>(&mut Unstructured::new(&x)).unwrap(),
+ &[2, 4, 6, 8, 1]
+ );
+ assert_eq!(
+ &*checked_arbitrary::<Arc<[u8]>>(&mut Unstructured::new(&x)).unwrap(),
+ &[2, 4, 6, 8, 1]
+ );
+ assert_eq!(
+ &*checked_arbitrary::<Rc<[u8]>>(&mut Unstructured::new(&x)).unwrap(),
+ &[2, 4, 6, 8, 1]
+ );
+ assert_eq!(
+ checked_arbitrary::<Vec<u32>>(&mut Unstructured::new(&x)).unwrap(),
+ &[84148994]
+ );
+ assert_eq!(
+ checked_arbitrary::<String>(&mut Unstructured::new(&x)).unwrap(),
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03"
+ );
+}
+
+#[test]
+fn arbitrary_take_rest() {
+ // Basic examples
+ let x = [1, 2, 3, 4];
+ assert_eq!(
+ checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&x)).unwrap(),
+ &[1, 2, 3, 4]
+ );
+ assert_eq!(
+ checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&x)).unwrap(),
+ &[2, 4]
+ );
+ assert_eq!(
+ &*checked_arbitrary_take_rest::<Box<[u8]>>(Unstructured::new(&x)).unwrap(),
+ &[2, 4]
+ );
+ assert_eq!(
+ &*checked_arbitrary_take_rest::<Arc<[u8]>>(Unstructured::new(&x)).unwrap(),
+ &[2, 4]
+ );
+ assert_eq!(
+ &*checked_arbitrary_take_rest::<Rc<[u8]>>(Unstructured::new(&x)).unwrap(),
+ &[2, 4]
+ );
+ assert_eq!(
+ checked_arbitrary_take_rest::<Vec<u32>>(Unstructured::new(&x)).unwrap(),
+ &[0x040302]
+ );
+ assert_eq!(
+ checked_arbitrary_take_rest::<String>(Unstructured::new(&x)).unwrap(),
+ "\x01\x02\x03\x04"
+ );
+
+ // Empty remainder
+ assert_eq!(
+ checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&[])).unwrap(),
+ &[]
+ );
+ assert_eq!(
+ checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&[])).unwrap(),
+ &[]
+ );
+
+ // Cannot consume all but can consume part of the input
+ assert_eq!(
+ checked_arbitrary_take_rest::<String>(Unstructured::new(&[1, 0xFF, 2])).unwrap(),
+ "\x01"
+ );
+}
+
+#[test]
+fn size_hint_for_tuples() {
+ assert_eq!(
+ (7, Some(7)),
+ <(bool, u16, i32) as Arbitrary<'_>>::size_hint(0)
+ );
+ assert_eq!((1, None), <(u8, Vec<u8>) as Arbitrary>::size_hint(0));
+}
diff --git a/crates/arbitrary/src/unstructured.rs b/crates/arbitrary/src/unstructured.rs
index 0bfdff2..8b7da26 100644
--- a/crates/arbitrary/src/unstructured.rs
+++ b/crates/arbitrary/src/unstructured.rs
@@ -68,6 +68,7 @@
/// }
/// # }
/// ```
+#[derive(Debug)]
pub struct Unstructured<'a> {
data: &'a [u8],
}
@@ -236,20 +237,20 @@
// We only consume as many bytes as necessary to cover the entire
// range of the byte string.
- // Note: We cast to u64 so we don't overflow when checking std::u32::MAX + 4 on 32-bit archs
- let len = if self.data.len() as u64 <= std::u8::MAX as u64 + 1 {
+ // Note: We cast to u64 so we don't overflow when checking u32::MAX + 4 on 32-bit archs
+ let len = if self.data.len() as u64 <= u8::MAX as u64 + 1 {
let bytes = 1;
let max_size = self.data.len() - bytes;
let (rest, for_size) = self.data.split_at(max_size);
self.data = rest;
Self::int_in_range_impl(0..=max_size as u8, for_size.iter().copied())?.0 as usize
- } else if self.data.len() as u64 <= std::u16::MAX as u64 + 2 {
+ } else if self.data.len() as u64 <= u16::MAX as u64 + 2 {
let bytes = 2;
let max_size = self.data.len() - bytes;
let (rest, for_size) = self.data.split_at(max_size);
self.data = rest;
Self::int_in_range_impl(0..=max_size as u16, for_size.iter().copied())?.0 as usize
- } else if self.data.len() as u64 <= std::u32::MAX as u64 + 4 {
+ } else if self.data.len() as u64 <= u32::MAX as u64 + 4 {
let bytes = 4;
let max_size = self.data.len() - bytes;
let (rest, for_size) = self.data.split_at(max_size);
@@ -280,15 +281,16 @@
/// # Example
///
/// ```
+ /// # fn foo() -> arbitrary::Result<()> {
/// use arbitrary::{Arbitrary, Unstructured};
///
/// let mut u = Unstructured::new(&[1, 2, 3, 4]);
///
- /// let x: i32 = u.int_in_range(-5_000..=-1_000)
- /// .expect("constructed `u` with enough bytes to generate an `i32`");
+ /// let x: i32 = u.int_in_range(-5_000..=-1_000)?;
///
/// assert!(-5_000 <= x);
/// assert!(x <= -1_000);
+ /// # Ok(()) }
/// ```
pub fn int_in_range<T>(&mut self, range: ops::RangeInclusive<T>) -> Result<T>
where
@@ -410,6 +412,41 @@
Ok(&choices[idx])
}
+ /// Choose one of the given iterator choices.
+ ///
+ /// This should only be used inside of `Arbitrary` implementations.
+ ///
+ /// Returns an error if there is not enough underlying data to make a
+ /// choice or if no choices are provided.
+ ///
+ /// # Examples
+ ///
+ /// Selecting a random item from a set:
+ ///
+ /// ```
+ /// use std::collections::BTreeSet;
+ /// use arbitrary::Unstructured;
+ ///
+ /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
+ /// let set = BTreeSet::from(['a', 'b', 'c']);
+ ///
+ /// let choice = u.choose_iter(set.iter()).unwrap();
+ ///
+ /// println!("chose {}", choice);
+ /// ```
+ pub fn choose_iter<T, I>(&mut self, choices: I) -> Result<T>
+ where
+ I: IntoIterator<Item = T>,
+ I::IntoIter: ExactSizeIterator,
+ {
+ let mut choices = choices.into_iter();
+ let idx = self.choose_index(choices.len())?;
+ let choice = choices
+ .nth(idx)
+ .expect("ExactSizeIterator should have correct len");
+ Ok(choice)
+ }
+
/// Choose a value in `0..len`.
///
/// Returns an error if the `len` is zero.
@@ -620,14 +657,8 @@
pub fn arbitrary_take_rest_iter<ElementType: Arbitrary<'a>>(
self,
) -> Result<ArbitraryTakeRestIter<'a, ElementType>> {
- let (lower, upper) = ElementType::size_hint(0);
-
- let elem_size = upper.unwrap_or(lower * 2);
- let elem_size = std::cmp::max(1, elem_size);
- let size = self.len() / elem_size;
Ok(ArbitraryTakeRestIter {
- size,
- u: Some(self),
+ u: self,
_marker: PhantomData,
})
}
@@ -735,25 +766,16 @@
/// Utility iterator produced by [`Unstructured::arbitrary_take_rest_iter`]
pub struct ArbitraryTakeRestIter<'a, ElementType> {
- u: Option<Unstructured<'a>>,
- size: usize,
+ u: Unstructured<'a>,
_marker: PhantomData<ElementType>,
}
impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, ElementType> {
type Item = Result<ElementType>;
fn next(&mut self) -> Option<Result<ElementType>> {
- if let Some(mut u) = self.u.take() {
- if self.size == 1 {
- Some(Arbitrary::arbitrary_take_rest(u))
- } else if self.size == 0 {
- None
- } else {
- self.size -= 1;
- let ret = Arbitrary::arbitrary(&mut u);
- self.u = Some(u);
- Some(ret)
- }
+ let keep_going = self.u.arbitrary().unwrap_or(false);
+ if keep_going {
+ Some(Arbitrary::arbitrary(&mut self.u))
} else {
None
}
@@ -890,8 +912,7 @@
// Should take one byte off the end
assert_eq!(u.arbitrary_byte_size().unwrap(), 6);
assert_eq!(u.len(), 9);
- let mut v = vec![];
- v.resize(260, 0);
+ let mut v = vec![0; 260];
v.push(1);
v.push(4);
let mut u = Unstructured::new(&v);
diff --git a/crates/arbitrary/tests/bound.rs b/crates/arbitrary/tests/bound.rs
new file mode 100644
index 0000000..7a772ac
--- /dev/null
+++ b/crates/arbitrary/tests/bound.rs
@@ -0,0 +1,142 @@
+#![cfg(feature = "derive")]
+
+use arbitrary::{Arbitrary, Unstructured};
+
+fn arbitrary_from<'a, T: Arbitrary<'a>>(input: &'a [u8]) -> T {
+ let mut buf = Unstructured::new(input);
+ T::arbitrary(&mut buf).expect("can create arbitrary instance OK")
+}
+
+/// This wrapper trait *implies* `Arbitrary`, but the compiler isn't smart enough to work that out
+/// so when using this wrapper we *must* opt-out of the auto-generated `T: Arbitrary` bounds.
+pub trait WrapperTrait: for<'a> Arbitrary<'a> {}
+
+impl WrapperTrait for u32 {}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait")]
+struct GenericSingleBound<T: WrapperTrait> {
+ t: T,
+}
+
+#[test]
+fn single_bound() {
+ let v: GenericSingleBound<u32> = arbitrary_from(&[0, 0, 0, 0]);
+ assert_eq!(v.t, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait, U: WrapperTrait")]
+struct GenericMultipleBoundsSingleAttribute<T: WrapperTrait, U: WrapperTrait> {
+ t: T,
+ u: U,
+}
+
+#[test]
+fn multiple_bounds_single_attribute() {
+ let v: GenericMultipleBoundsSingleAttribute<u32, u32> =
+ arbitrary_from(&[1, 0, 0, 0, 2, 0, 0, 0]);
+ assert_eq!(v.t, 1);
+ assert_eq!(v.u, 2);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait")]
+#[arbitrary(bound = "U: Default")]
+struct GenericMultipleArbitraryAttributes<T: WrapperTrait, U: Default> {
+ t: T,
+ #[arbitrary(default)]
+ u: U,
+}
+
+#[test]
+fn multiple_arbitrary_attributes() {
+ let v: GenericMultipleArbitraryAttributes<u32, u32> = arbitrary_from(&[1, 0, 0, 0]);
+ assert_eq!(v.t, 1);
+ assert_eq!(v.u, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait", bound = "U: Default")]
+struct GenericMultipleBoundAttributes<T: WrapperTrait, U: Default> {
+ t: T,
+ #[arbitrary(default)]
+ u: U,
+}
+
+#[test]
+fn multiple_bound_attributes() {
+ let v: GenericMultipleBoundAttributes<u32, u32> = arbitrary_from(&[1, 0, 0, 0]);
+ assert_eq!(v.t, 1);
+ assert_eq!(v.u, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: WrapperTrait", bound = "U: Default")]
+#[arbitrary(bound = "V: WrapperTrait, W: Default")]
+struct GenericMultipleArbitraryAndBoundAttributes<
+ T: WrapperTrait,
+ U: Default,
+ V: WrapperTrait,
+ W: Default,
+> {
+ t: T,
+ #[arbitrary(default)]
+ u: U,
+ v: V,
+ #[arbitrary(default)]
+ w: W,
+}
+
+#[test]
+fn multiple_arbitrary_and_bound_attributes() {
+ let v: GenericMultipleArbitraryAndBoundAttributes<u32, u32, u32, u32> =
+ arbitrary_from(&[1, 0, 0, 0, 2, 0, 0, 0]);
+ assert_eq!(v.t, 1);
+ assert_eq!(v.u, 0);
+ assert_eq!(v.v, 2);
+ assert_eq!(v.w, 0);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "T: Default")]
+struct GenericDefault<T: Default> {
+ #[arbitrary(default)]
+ x: T,
+}
+
+#[test]
+fn default_bound() {
+ // We can write a generic func without any `Arbitrary` bound.
+ fn generic_default<T: Default>() -> GenericDefault<T> {
+ arbitrary_from(&[])
+ }
+
+ assert_eq!(generic_default::<u64>().x, 0);
+ assert_eq!(generic_default::<String>().x, String::new());
+ assert_eq!(generic_default::<Vec<u8>>().x, Vec::new());
+}
+
+#[derive(Arbitrary)]
+#[arbitrary()]
+struct EmptyArbitraryAttribute {
+ t: u32,
+}
+
+#[test]
+fn empty_arbitrary_attribute() {
+ let v: EmptyArbitraryAttribute = arbitrary_from(&[1, 0, 0, 0]);
+ assert_eq!(v.t, 1);
+}
+
+#[derive(Arbitrary)]
+#[arbitrary(bound = "")]
+struct EmptyBoundAttribute {
+ t: u32,
+}
+
+#[test]
+fn empty_bound_attribute() {
+ let v: EmptyBoundAttribute = arbitrary_from(&[1, 0, 0, 0]);
+ assert_eq!(v.t, 1);
+}
diff --git a/crates/arbitrary/tests/derive.rs b/crates/arbitrary/tests/derive.rs
index f29d227..880d146 100644
--- a/crates/arbitrary/tests/derive.rs
+++ b/crates/arbitrary/tests/derive.rs
@@ -2,6 +2,9 @@
// Various structs/fields that we are deriving `Arbitrary` for aren't actually
// used except to exercise the derive.
#![allow(dead_code)]
+// Various assert_eq! are used to compare result of bool amongst other data types
+// In this case, using assert! is less explicit and readable
+#![allow(clippy::bool_assert_comparison)]
use arbitrary::*;
@@ -59,7 +62,7 @@
assert_eq!(s2.1, true);
assert_eq!(s1.2, 0x4030201);
assert_eq!(s2.2, 0x4030201);
- assert_eq!(s1.3, vec![0x605, 0x807]);
+ assert_eq!(s1.3, vec![0x0706]);
assert_eq!(s2.3, "\x05\x06\x07\x08");
}
@@ -116,6 +119,30 @@
assert_eq!((4, Some(17)), <MyEnum as Arbitrary>::size_hint(0));
}
+// This should result in a compiler-error:
+// #[derive(Arbitrary, Debug)]
+// enum Never {
+// #[arbitrary(skip)]
+// Nope,
+// }
+
+#[derive(Arbitrary, Debug)]
+enum SkipVariant {
+ Always,
+ #[arbitrary(skip)]
+ Never,
+}
+
+#[test]
+fn test_skip_variant() {
+ (0..=u8::MAX).for_each(|byte| {
+ let buffer = [byte];
+ let unstructured = Unstructured::new(&buffer);
+ let skip_variant = SkipVariant::arbitrary_take_rest(unstructured).unwrap();
+ assert!(!matches!(skip_variant, SkipVariant::Never));
+ })
+}
+
#[derive(Arbitrary, Debug)]
enum RecursiveTree {
Leaf,
@@ -125,13 +152,86 @@
},
}
+#[derive(Arbitrary, Debug)]
+struct WideRecursiveStruct {
+ a: Option<Box<WideRecursiveStruct>>,
+ b: Option<Box<WideRecursiveStruct>>,
+ c: Option<Box<WideRecursiveStruct>>,
+ d: Option<Box<WideRecursiveStruct>>,
+ e: Option<Box<WideRecursiveStruct>>,
+ f: Option<Box<WideRecursiveStruct>>,
+ g: Option<Box<WideRecursiveStruct>>,
+ h: Option<Box<WideRecursiveStruct>>,
+ i: Option<Box<WideRecursiveStruct>>,
+ k: Option<Box<WideRecursiveStruct>>,
+}
+
+#[derive(Arbitrary, Debug)]
+enum WideRecursiveEnum {
+ None,
+ A(Box<WideRecursiveStruct>),
+ B(Box<WideRecursiveStruct>),
+ C(Box<WideRecursiveStruct>),
+ D(Box<WideRecursiveStruct>),
+ E(Box<WideRecursiveStruct>),
+ F(Box<WideRecursiveStruct>),
+ G(Box<WideRecursiveStruct>),
+ H(Box<WideRecursiveStruct>),
+ I(Box<WideRecursiveStruct>),
+ K(Box<WideRecursiveStruct>),
+}
+
+#[derive(Arbitrary, Debug)]
+enum WideRecursiveMixedEnum {
+ None,
+ A(Box<WideRecursiveMixedEnum>),
+ B(Box<WideRecursiveMixedEnum>),
+ C(Box<WideRecursiveMixedEnum>),
+ D(Box<WideRecursiveMixedEnum>),
+ E(Box<WideRecursiveMixedEnum>),
+ F(Box<WideRecursiveMixedStruct>),
+ G(Box<WideRecursiveMixedStruct>),
+ H(Box<WideRecursiveMixedStruct>),
+ I(Box<WideRecursiveMixedStruct>),
+ K(Box<WideRecursiveMixedStruct>),
+}
+
+#[derive(Arbitrary, Debug)]
+struct WideRecursiveMixedStruct {
+ a: Option<Box<WideRecursiveMixedEnum>>,
+ b: Option<Box<WideRecursiveMixedEnum>>,
+ c: Option<Box<WideRecursiveMixedEnum>>,
+ d: Option<Box<WideRecursiveMixedEnum>>,
+ e: Option<Box<WideRecursiveMixedEnum>>,
+ f: Option<Box<WideRecursiveMixedStruct>>,
+ g: Option<Box<WideRecursiveMixedStruct>>,
+ h: Option<Box<WideRecursiveMixedStruct>>,
+ i: Option<Box<WideRecursiveMixedStruct>>,
+ k: Option<Box<WideRecursiveMixedStruct>>,
+}
+
#[test]
fn recursive() {
let raw = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
let _rec: RecursiveTree = arbitrary_from(&raw);
+ let _rec: WideRecursiveStruct = arbitrary_from(&raw);
+ let _rec: WideRecursiveEnum = arbitrary_from(&raw);
+ let _rec: WideRecursiveMixedStruct = arbitrary_from(&raw);
+ let _rec: WideRecursiveMixedEnum = arbitrary_from(&raw);
+
+ assert_eq!((0, None), <WideRecursiveStruct as Arbitrary>::size_hint(0));
+ assert_eq!((0, None), <WideRecursiveEnum as Arbitrary>::size_hint(0));
+ assert_eq!(
+ (0, None),
+ <WideRecursiveMixedStruct as Arbitrary>::size_hint(0)
+ );
+ assert_eq!(
+ (0, None),
+ <WideRecursiveMixedEnum as Arbitrary>::size_hint(0)
+ );
let (lower, upper) = <RecursiveTree as Arbitrary>::size_hint(0);
- assert_eq!(lower, 4, "need a u32 for the discriminant at minimum");
+ assert_eq!(lower, 0, "Cannot compute size hint of recursive structure");
assert!(
upper.is_none(),
"potentially infinitely recursive, so no upper bound"
@@ -276,3 +376,20 @@
// 17 is the 3rd byte used by arbitrary
assert_eq!(parcel.price, 17);
}
+
+#[test]
+fn derive_structs_named_same_as_core() {
+ #[derive(Debug, Arbitrary)]
+ struct Option {
+ f: core::option::Option<u32>,
+ }
+
+ let _ = Option::arbitrary(&mut Unstructured::new(&[]));
+
+ #[derive(Debug, Default, Arbitrary)]
+ struct Default {
+ f: u32,
+ }
+
+ let _ = Default::arbitrary(&mut Unstructured::new(&[]));
+}
diff --git a/crates/derive_arbitrary/.android-checksum.json b/crates/derive_arbitrary/.android-checksum.json
index d0cd919..3c8eca9 100644
--- a/crates/derive_arbitrary/.android-checksum.json
+++ b/crates/derive_arbitrary/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{"METADATA":"c74f9be53fa880bfaaa35e791e79d8f0a0e89a8a4a93b1340f19819cb149fa67","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"94b361e8062eac8c5297a235595146e0758c2fe96a573776ae6022bcaf67508a","Android.bp":"ed61c2a0bf67f4a7c8b7bfba3558c9b875a46485140d15929e8b577a50a5583b","LICENSE-MIT":"3b270c5247fc2265dac124e9ada8609aa00c03289761bafa13c741aee60a504a","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e",".cargo-checksum.json":"ea186c95063a5399571e9174af2f4b96047e0d6b93e2514894d1a252d3a512dd","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","src/lib.rs":"bf338d09989ad2e416d4cc8d6155e9c331f9f09a49eccbbc56e9925ce29465b5","src/field_attributes.rs":"cdb0ba9cd0b22fa3b6e2df50f86e777347940a731ac460312e64cd68ba11d13c","Cargo.toml":"67eb474e4cc4913e74be742c354d1764ee8c076d6e7132dfa0af8cffb1bb4ef1","src/container_attributes.rs":"b3132133bb66fa6f34ca83d6f2c4e8e8c85b7719ddf41b33ab4724e5af38b722","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","TEST_MAPPING":"02e3608ad87e3ec7ed78e24e48f33b3312730de4e48675e89582aa561ddf50e7"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"11605a10285a8ed21fda31ba208e50306d7f2569fb4b3935d06964dc2fdf4415","Android.bp":"a34ee3fea0ad44c6b81bb02097dc8f4c9419e8828e013dd5555d5436e5685bc6","Cargo.toml":"fd7d6fff3744f32c795682a41adc114fc36631923ecb5421384577df49d86c16","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"3b270c5247fc2265dac124e9ada8609aa00c03289761bafa13c741aee60a504a","METADATA":"12dfe0f773e9ac7f603027bfeaeb396873bc7cfc108f048f5fe3a430d263eccb","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"94b361e8062eac8c5297a235595146e0758c2fe96a573776ae6022bcaf67508a","TEST_MAPPING":"02e3608ad87e3ec7ed78e24e48f33b3312730de4e48675e89582aa561ddf50e7","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e","src/container_attributes.rs":"b3132133bb66fa6f34ca83d6f2c4e8e8c85b7719ddf41b33ab4724e5af38b722","src/field_attributes.rs":"cdb0ba9cd0b22fa3b6e2df50f86e777347940a731ac460312e64cd68ba11d13c","src/lib.rs":"ff97d54d566d39a62d5d1e9bd037081c5f51267f7a5681dc6bedc1f4fc5f6128","src/variant_attributes.rs":"28cca42e56d167dff434c0fe2e0f07ba1bc72039738c55b59d4a5502cf9bea3b"}}
\ No newline at end of file
diff --git a/crates/derive_arbitrary/.cargo-checksum.json b/crates/derive_arbitrary/.cargo-checksum.json
index 00d7243..9c165e8 100644
--- a/crates/derive_arbitrary/.cargo-checksum.json
+++ b/crates/derive_arbitrary/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"196abb2a00d047e16ba1337cd492b4115ebcee941283acde6fb80dc025176dce","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"7059db284b2016ba7355c63a2b14eb732c7b8952286ff1bc4fdde605018a39c4","src/container_attributes.rs":"9342a89e5e5f412159d1a1a88ae4ee0248180f30adc13e61ebf5f96b5f09877f","src/field_attributes.rs":"15093171d7f1e30c2b2523788e54c69c816029b310133ceb1ac811d1f11a76d5","src/lib.rs":"701a9c66e25c3a2151eab9159f6c2fa64ea910ceb6feb225dc97c2734254c265"},"package":"67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"}
\ No newline at end of file
+{"files":{"Cargo.toml":"f8a5e6d7f00bfbf9ed898cdb4164d04df28887c1cb9903cd8009c4e34a1be814","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"15656cc11a8331f28c0986b8ab97220d3e76f98e60ed388b5ffad37dfac4710c","README.md":"7059db284b2016ba7355c63a2b14eb732c7b8952286ff1bc4fdde605018a39c4","src/container_attributes.rs":"9342a89e5e5f412159d1a1a88ae4ee0248180f30adc13e61ebf5f96b5f09877f","src/field_attributes.rs":"15093171d7f1e30c2b2523788e54c69c816029b310133ceb1ac811d1f11a76d5","src/lib.rs":"ecc4eddd09472d5e06eef8f6d0092bee896abcad168391e950e18cf383135e1c","src/variant_attributes.rs":"01428d7c9db83d06e6b846e9057352e830f9d45090c6cb21415426c4bab9df09"},"package":"30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"}
\ No newline at end of file
diff --git a/crates/derive_arbitrary/Android.bp b/crates/derive_arbitrary/Android.bp
index 8144ca5..a8c2779 100644
--- a/crates/derive_arbitrary/Android.bp
+++ b/crates/derive_arbitrary/Android.bp
@@ -17,7 +17,7 @@
name: "libderive_arbitrary",
crate_name: "derive_arbitrary",
cargo_env_compat: true,
- cargo_pkg_version: "1.3.2",
+ cargo_pkg_version: "1.4.1",
crate_root: "src/lib.rs",
edition: "2021",
rustlibs: [
diff --git a/crates/derive_arbitrary/Cargo.toml b/crates/derive_arbitrary/Cargo.toml
index 46e86fd..5a06e0c 100644
--- a/crates/derive_arbitrary/Cargo.toml
+++ b/crates/derive_arbitrary/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.63.0"
name = "derive_arbitrary"
-version = "1.3.2"
+version = "1.4.1"
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <[email protected]>",
@@ -21,6 +21,11 @@
"Andre Bogus <[email protected]>",
"Corey Farwell <[email protected]>",
]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
description = "Derives arbitrary traits"
documentation = "https://docs.rs/arbitrary/"
readme = "README.md"
@@ -35,7 +40,9 @@
repository = "https://github.com/rust-fuzz/arbitrary"
[lib]
-proc_macro = true
+name = "derive_arbitrary"
+path = "src/lib.rs"
+proc-macro = true
[dependencies.proc-macro2]
version = "1.0"
@@ -48,4 +55,5 @@
features = [
"derive",
"parsing",
+ "extra-traits",
]
diff --git a/crates/derive_arbitrary/METADATA b/crates/derive_arbitrary/METADATA
index 23ddd77..b24118b 100644
--- a/crates/derive_arbitrary/METADATA
+++ b/crates/derive_arbitrary/METADATA
@@ -1,17 +1,17 @@
name: "derive_arbitrary"
description: "Derives arbitrary traits"
third_party {
- version: "1.3.2"
+ version: "1.4.1"
license_type: NOTICE
last_upgrade_date {
- year: 2024
- month: 2
- day: 8
+ year: 2025
+ month: 1
+ day: 7
}
homepage: "https://crates.io/crates/derive_arbitrary"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.3.2.crate"
- version: "1.3.2"
+ value: "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.4.1.crate"
+ version: "1.4.1"
}
}
diff --git a/crates/derive_arbitrary/src/lib.rs b/crates/derive_arbitrary/src/lib.rs
index 886bb49..81c0274 100644
--- a/crates/derive_arbitrary/src/lib.rs
+++ b/crates/derive_arbitrary/src/lib.rs
@@ -6,11 +6,14 @@
mod container_attributes;
mod field_attributes;
+mod variant_attributes;
+
use container_attributes::ContainerAttributes;
use field_attributes::{determine_field_constructor, FieldConstructor};
+use variant_attributes::not_skipped;
-static ARBITRARY_ATTRIBUTE_NAME: &str = "arbitrary";
-static ARBITRARY_LIFETIME_NAME: &str = "'arbitrary";
+const ARBITRARY_ATTRIBUTE_NAME: &str = "arbitrary";
+const ARBITRARY_LIFETIME_NAME: &str = "'arbitrary";
#[proc_macro_derive(Arbitrary, attributes(arbitrary))]
pub fn derive_arbitrary(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
@@ -55,9 +58,9 @@
Ok(quote! {
const _: () = {
- std::thread_local! {
+ ::std::thread_local! {
#[allow(non_upper_case_globals)]
- static #recursive_count: std::cell::Cell<u32> = std::cell::Cell::new(0);
+ static #recursive_count: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);
}
#[automatically_derived]
@@ -201,80 +204,107 @@
})
}
+ fn arbitrary_variant(
+ index: u64,
+ enum_name: &Ident,
+ variant_name: &Ident,
+ ctor: TokenStream,
+ ) -> TokenStream {
+ quote! { #index => #enum_name::#variant_name #ctor }
+ }
+
+ fn arbitrary_enum_method(
+ recursive_count: &syn::Ident,
+ unstructured: TokenStream,
+ variants: &[TokenStream],
+ ) -> impl quote::ToTokens {
+ let count = variants.len() as u64;
+ with_recursive_count_guard(
+ recursive_count,
+ quote! {
+ // Use a multiply + shift to generate a ranged random number
+ // with slight bias. For details, see:
+ // https://lemire.me/blog/2016/06/30/fast-random-shuffling
+ Ok(match (u64::from(<u32 as arbitrary::Arbitrary>::arbitrary(#unstructured)?) * #count) >> 32 {
+ #(#variants,)*
+ _ => unreachable!()
+ })
+ },
+ )
+ }
+
+ fn arbitrary_enum(
+ DataEnum { variants, .. }: &DataEnum,
+ enum_name: &Ident,
+ lifetime: LifetimeParam,
+ recursive_count: &syn::Ident,
+ ) -> Result<TokenStream> {
+ let filtered_variants = variants.iter().filter(not_skipped);
+
+ // Check attributes of all variants:
+ filtered_variants
+ .clone()
+ .try_for_each(check_variant_attrs)?;
+
+ // From here on, we can assume that the attributes of all variants were checked.
+ let enumerated_variants = filtered_variants
+ .enumerate()
+ .map(|(index, variant)| (index as u64, variant));
+
+ // Construct `match`-arms for the `arbitrary` method.
+ let variants = enumerated_variants
+ .clone()
+ .map(|(index, Variant { fields, ident, .. })| {
+ construct(fields, |_, field| gen_constructor_for_field(field))
+ .map(|ctor| arbitrary_variant(index, enum_name, ident, ctor))
+ })
+ .collect::<Result<Vec<TokenStream>>>()?;
+
+ // Construct `match`-arms for the `arbitrary_take_rest` method.
+ let variants_take_rest = enumerated_variants
+ .map(|(index, Variant { fields, ident, .. })| {
+ construct_take_rest(fields)
+ .map(|ctor| arbitrary_variant(index, enum_name, ident, ctor))
+ })
+ .collect::<Result<Vec<TokenStream>>>()?;
+
+ // Most of the time, `variants` is not empty (the happy path),
+ // thus `variants_take_rest` will be used,
+ // so no need to move this check before constructing `variants_take_rest`.
+ // If `variants` is empty, this will emit a compiler-error.
+ (!variants.is_empty())
+ .then(|| {
+ // TODO: Improve dealing with `u` vs. `&mut u`.
+ let arbitrary = arbitrary_enum_method(recursive_count, quote! { u }, &variants);
+ let arbitrary_take_rest = arbitrary_enum_method(recursive_count, quote! { &mut u }, &variants_take_rest);
+
+ quote! {
+ fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
+ #arbitrary
+ }
+
+ fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
+ #arbitrary_take_rest
+ }
+ }
+ })
+ .ok_or_else(|| Error::new_spanned(
+ enum_name,
+ "Enum must have at least one variant, that is not skipped"
+ ))
+ }
+
let ident = &input.ident;
- let output = match &input.data {
- Data::Struct(data) => arbitrary_structlike(&data.fields, ident, lifetime, recursive_count)?,
+ match &input.data {
+ Data::Struct(data) => arbitrary_structlike(&data.fields, ident, lifetime, recursive_count),
Data::Union(data) => arbitrary_structlike(
&Fields::Named(data.fields.clone()),
ident,
lifetime,
recursive_count,
- )?,
- Data::Enum(data) => {
- let variants: Vec<TokenStream> = data
- .variants
- .iter()
- .enumerate()
- .map(|(i, variant)| {
- let idx = i as u64;
- let variant_name = &variant.ident;
- construct(&variant.fields, |_, field| gen_constructor_for_field(field))
- .map(|ctor| quote! { #idx => #ident::#variant_name #ctor })
- })
- .collect::<Result<_>>()?;
-
- let variants_take_rest: Vec<TokenStream> = data
- .variants
- .iter()
- .enumerate()
- .map(|(i, variant)| {
- let idx = i as u64;
- let variant_name = &variant.ident;
- construct_take_rest(&variant.fields)
- .map(|ctor| quote! { #idx => #ident::#variant_name #ctor })
- })
- .collect::<Result<_>>()?;
-
- let count = data.variants.len() as u64;
-
- let arbitrary = with_recursive_count_guard(
- recursive_count,
- quote! {
- // Use a multiply + shift to generate a ranged random number
- // with slight bias. For details, see:
- // https://lemire.me/blog/2016/06/30/fast-random-shuffling
- Ok(match (u64::from(<u32 as arbitrary::Arbitrary>::arbitrary(u)?) * #count) >> 32 {
- #(#variants,)*
- _ => unreachable!()
- })
- },
- );
-
- let arbitrary_take_rest = with_recursive_count_guard(
- recursive_count,
- quote! {
- // Use a multiply + shift to generate a ranged random number
- // with slight bias. For details, see:
- // https://lemire.me/blog/2016/06/30/fast-random-shuffling
- Ok(match (u64::from(<u32 as arbitrary::Arbitrary>::arbitrary(&mut u)?) * #count) >> 32 {
- #(#variants_take_rest,)*
- _ => unreachable!()
- })
- },
- );
-
- quote! {
- fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
- #arbitrary
- }
-
- fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
- #arbitrary_take_rest
- }
- }
- }
- };
- Ok(output)
+ ),
+ Data::Enum(data) => arbitrary_enum(data, ident, lifetime, recursive_count),
+ }
}
fn construct(
@@ -311,7 +341,7 @@
fn construct_take_rest(fields: &Fields) -> Result<TokenStream> {
construct(fields, |idx, field| {
determine_field_constructor(field).map(|field_constructor| match field_constructor {
- FieldConstructor::Default => quote!(Default::default()),
+ FieldConstructor::Default => quote!(::core::default::Default::default()),
FieldConstructor::Arbitrary => {
if idx + 1 == fields.len() {
quote! { arbitrary::Arbitrary::arbitrary_take_rest(u)? }
@@ -334,17 +364,17 @@
determine_field_constructor(f).map(|field_constructor| {
match field_constructor {
FieldConstructor::Default | FieldConstructor::Value(_) => {
- quote!((0, Some(0)))
+ quote!(Ok((0, Some(0))))
}
FieldConstructor::Arbitrary => {
- quote! { <#ty as arbitrary::Arbitrary>::size_hint(depth) }
+ quote! { <#ty as arbitrary::Arbitrary>::try_size_hint(depth) }
}
// Note that in this case it's hard to determine what size_hint must be, so size_of::<T>() is
// just an educated guess, although it's gonna be inaccurate for dynamically
// allocated types (Vec, HashMap, etc.).
FieldConstructor::With(_) => {
- quote! { (::core::mem::size_of::<#ty>(), None) }
+ quote! { Ok((::core::mem::size_of::<#ty>(), None)) }
}
}
})
@@ -352,9 +382,9 @@
.collect::<Result<Vec<TokenStream>>>()
.map(|hints| {
quote! {
- arbitrary::size_hint::and_all(&[
- #( #hints ),*
- ])
+ Ok(arbitrary::size_hint::and_all(&[
+ #( #hints? ),*
+ ]))
}
})
};
@@ -362,8 +392,13 @@
size_hint_fields(fields).map(|hint| {
quote! {
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- arbitrary::size_hint::recursion_guard(depth, |depth| #hint)
+ fn size_hint(depth: usize) -> (usize, ::core::option::Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
+
+ #[inline]
+ fn try_size_hint(depth: usize) -> ::core::result::Result<(usize, ::core::option::Option<usize>), arbitrary::MaxRecursionReached> {
+ arbitrary::size_hint::try_recursion_guard(depth, |depth| #hint)
}
}
})
@@ -374,18 +409,26 @@
Data::Enum(data) => data
.variants
.iter()
- .map(|v| size_hint_fields(&v.fields))
+ .filter(not_skipped)
+ .map(|Variant { fields, .. }| {
+ // The attributes of all variants are checked in `gen_arbitrary_method` above
+ // and can therefore assume that they are valid.
+ size_hint_fields(fields)
+ })
.collect::<Result<Vec<TokenStream>>>()
.map(|variants| {
quote! {
+ fn size_hint(depth: usize) -> (usize, ::core::option::Option<usize>) {
+ Self::try_size_hint(depth).unwrap_or_default()
+ }
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- arbitrary::size_hint::and(
- <u32 as arbitrary::Arbitrary>::size_hint(depth),
- arbitrary::size_hint::recursion_guard(depth, |depth| {
- arbitrary::size_hint::or_all(&[ #( #variants ),* ])
- }),
- )
+ fn try_size_hint(depth: usize) -> ::core::result::Result<(usize, ::core::option::Option<usize>), arbitrary::MaxRecursionReached> {
+ Ok(arbitrary::size_hint::and(
+ <u32 as arbitrary::Arbitrary>::try_size_hint(depth)?,
+ arbitrary::size_hint::try_recursion_guard(depth, |depth| {
+ Ok(arbitrary::size_hint::or_all(&[ #( #variants? ),* ]))
+ })?,
+ ))
}
}
}),
@@ -394,10 +437,25 @@
fn gen_constructor_for_field(field: &Field) -> Result<TokenStream> {
let ctor = match determine_field_constructor(field)? {
- FieldConstructor::Default => quote!(Default::default()),
+ FieldConstructor::Default => quote!(::core::default::Default::default()),
FieldConstructor::Arbitrary => quote!(arbitrary::Arbitrary::arbitrary(u)?),
FieldConstructor::With(function_or_closure) => quote!((#function_or_closure)(u)?),
FieldConstructor::Value(value) => quote!(#value),
};
Ok(ctor)
}
+
+fn check_variant_attrs(variant: &Variant) -> Result<()> {
+ for attr in &variant.attrs {
+ if attr.path().is_ident(ARBITRARY_ATTRIBUTE_NAME) {
+ return Err(Error::new_spanned(
+ attr,
+ format!(
+ "invalid `{}` attribute. it is unsupported on enum variants. try applying it to a field of the variant instead",
+ ARBITRARY_ATTRIBUTE_NAME
+ ),
+ ));
+ }
+ }
+ Ok(())
+}
diff --git a/crates/derive_arbitrary/src/variant_attributes.rs b/crates/derive_arbitrary/src/variant_attributes.rs
new file mode 100644
index 0000000..39957df
--- /dev/null
+++ b/crates/derive_arbitrary/src/variant_attributes.rs
@@ -0,0 +1,21 @@
+use crate::ARBITRARY_ATTRIBUTE_NAME;
+use syn::*;
+
+pub fn not_skipped(variant: &&Variant) -> bool {
+ !should_skip(variant)
+}
+
+fn should_skip(Variant { attrs, .. }: &Variant) -> bool {
+ attrs
+ .iter()
+ .filter_map(|attr| {
+ attr.path()
+ .is_ident(ARBITRARY_ATTRIBUTE_NAME)
+ .then(|| attr.parse_args::<Meta>())
+ .and_then(Result::ok)
+ })
+ .any(|meta| match meta {
+ Meta::Path(path) => path.is_ident("skip"),
+ _ => false,
+ })
+}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index f91f7bd..39eaecd 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -606,9 +606,9 @@
[[package]]
name = "arbitrary"
-version = "1.2.3"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arc-swap"
@@ -1645,9 +1645,9 @@
[[package]]
name = "derive_arbitrary"
-version = "1.3.2"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
dependencies = [
"proc-macro2 1.0.92",
"quote 1.0.37",
@@ -1709,7 +1709,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
- "libloading 0.7.4",
+ "libloading 0.8.6",
]
[[package]]
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index ceec3fa..419e9da 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -16,7 +16,7 @@
annotate-snippets = "=0.9.2"
anstyle = "=1.0.10"
anyhow = "=1.0.79"
-arbitrary = "=1.2.3"
+arbitrary = "=1.4.1"
arc-swap = "=1.7.1"
argh = "=0.1.12"
argh_derive = "=0.1.12"
@@ -86,7 +86,7 @@
debug_tree = "=0.4.0"
der = "=0.7.9"
der_derive = "=0.7.3"
-derive_arbitrary = "=1.3.2"
+derive_arbitrary = "=1.4.1"
displaydoc = "=0.2.5"
document-features = "=0.2.10"
downcast = "=0.11.0"