diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 14c2d65..72f3d12 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,7 +7,7 @@ jobs: name: Vanilla Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - run: rustup update - name: Build run: cargo build --verbose --all @@ -17,7 +17,7 @@ jobs: name: All Features Enabled Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - run: rustup update - name: Build run: cargo build --verbose --all-features --all @@ -25,11 +25,31 @@ jobs: run: cargo test --verbose --all-features --all - name: Build Examples run: cargo build --examples --all-features --all + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: rustup update + - run: rustup component add clippy + - run: cargo clippy --all-features --workspace -- -Dclippy::all rustfmt: name: Check rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - run: rustup update - run: rustup component add rustfmt --toolchain stable - run: cargo +stable fmt --all -- --check + fuzz: + name: Run `int_in_range` fuzz target + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: rustup update + - name: "Install nightly" + run: rustup toolchain install nightly && rustup default nightly + - name: "Install `cargo-fuzz`" + run: cargo install cargo-fuzz + - name: "Fuzz for 3 minutes" + run: cargo fuzz run int_in_range -- -max_total_time=$((3 * 60)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fb42d8..2d4bb72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ Released YYYY-MM-DD. ### Fixed -* TODO (or remove section if none) +* (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) ### Security @@ -28,6 +28,227 @@ Released YYYY-MM-DD. -------------------------------------------------------------------------------- +## 1.2.3 + +Released 2023-01-20. + +### Fixed + +* The `derive(Arbitrary)` will now annotate the generated `impl`s with a `#[automatically_derived]` + attribute to indicate to e.g. clippy that lints should not fire for the code within the derived + implementation. + +## 1.2.2 + +Released 2023-01-03. + +### Fixed + +* Ensured that `arbitrary` and `derive_arbitrary` versions are synced up so that + they don't, e.g., emit generated code that depends on newer versions of + `arbitrary` than the one currently in + use. [#134](https://github.com/rust-fuzz/arbitrary/issues/134) + +## 1.2.1 + +### Fixed + +* Fixed an issue where `std::thread_local!` macro invocations in derive code + were not fully prefixed, causing confusing build errors in certain situations. + +## 1.2.0 + +Released 2022-10-20. + +### Added + +* Support custom arbitrary implementation for fields on + derive. [#129](https://github.com/rust-fuzz/arbitrary/pull/129) + +-------------------------------------------------------------------------------- + +## 1.1.6 + +Released 2022-09-08. + +### Fixed + +* Fixed a potential panic due to an off-by-one error in the `Arbitrary` + implementation for `std::ops::Bound`. + +-------------------------------------------------------------------------------- + +## 1.1.5 + +Released 2022-09-20. + +### Added + +* Implemented `Arbitrary` for `std::ops::Bound`. + +### Fixed + +* Fixed a bug where `Unstructured::int_in_range` could return out-of-range + integers when generating arbitrary signed integers. + +-------------------------------------------------------------------------------- + +## 1.1.4 + +Released 2022-08-29. + +### Added + +* Implemented `Arbitrary` for `Rc` and `Arc` + +### Changed + +* Allow overriding the error type in `arbitrary::Result` +* The `Unstructured::arbitrary_loop` method will consume fewer bytes of input + now. + +### Fixed + +* Fixed a bug where `Unstructured::int_in_range` could return out-of-range + integers. + +-------------------------------------------------------------------------------- + +## 1.1.3 + +Released 2022-06-23. + +### Fixed + +* Fixed some potential (but highly unlikely) name-clashes inside + `derive(Arbitrary)`'s generated + code. [#111](https://github.com/rust-fuzz/arbitrary/pull/111) +* Fixed an edge case where `derive(Arbitrary)` for recursive types that detected + an overflow would not reset the overflow + detection. [#111](https://github.com/rust-fuzz/arbitrary/pull/111) + +-------------------------------------------------------------------------------- + +## 1.1.2 + +Released 2022-06-16. + +### Fixed + +* Fixed a warning inside `derive(Arbitrary)`-generated + code. [#110](https://github.com/rust-fuzz/arbitrary/pull/110) + +-------------------------------------------------------------------------------- + +## 1.1.1 + +Released 2022-06-14. + +### Fixed + +* Fixed a stack overflow when using `derive(Arbitrary)` with recursive types and + empty inputs. [#109](https://github.com/rust-fuzz/arbitrary/pull/109) + +-------------------------------------------------------------------------------- + +## 1.1.0 + +Released 2022-02-09. + +### Added + +* Added the `Unstructured::ratio` method to generate a boolean that is `true` at + the given rate. + +* Added the `Unstructured::arbitrary_loop` method to call a function an + arbitrary number of times. + +-------------------------------------------------------------------------------- + +## 1.0.3 + +Released 2021-11-20. + +### Fixed + +* Fixed documentation for `Unstructured::fill_bytes`. We forgot to update this + way back in [#53](https://github.com/rust-fuzz/arbitrary/pull/53) when the + behavior changed. + +-------------------------------------------------------------------------------- + +## 1.0.2 + +Released 2021-08-25. + +### Added + +* `Arbitrary` impls for `HashMap`s and `HashSet`s with custom `Hasher`s + [#87](https://github.com/rust-fuzz/arbitrary/pull/87) + +-------------------------------------------------------------------------------- + +## 1.0.1 + +Released 2021-05-20. + +### Added + +* `Arbitrary` impls for `NonZeroX` types [#79](https://github.com/rust-fuzz/arbitrary/pull/79) +* `Arbitrary` impls for all arrays using const generics [#55](https://github.com/rust-fuzz/arbitrary/pull/55) +* `Arbitrary` impls for `Ipv4Addr` and `Ipv6Addr` [#84](https://github.com/rust-fuzz/arbitrary/pull/84) + +### Fixed + +* Use fewer bytes for `Unstructured::int_in_range()` [#80](https://github.com/rust-fuzz/arbitrary/pull/80) +* Use correct range for `char` generation [#83](https://github.com/rust-fuzz/arbitrary/pull/83) + +-------------------------------------------------------------------------------- + +## 1.0.0 + +Released 2020-02-24. + +See 1.0.0-rc1 and 1.0.0-rc2 for changes since 0.4.7, which was the last main +line release. + +-------------------------------------------------------------------------------- + +## 1.0.0-rc2 + +Released 2021-02-09. + +### Added + +* The `Arbitrary` trait is now implemented for `&[u8]`. [#67](https://github.com/rust-fuzz/arbitrary/pull/67) + +### Changed + +* Rename `Unstructured#get_bytes` to `Unstructured#bytes`. [#70](https://github.com/rust-fuzz/arbitrary/pull/70) +* Passing an empty slice of choices to `Unstructured#choose` returns an error. Previously it would panic. [71](https://github.com/rust-fuzz/arbitrary/pull/71) + +-------------------------------------------------------------------------------- + +## 1.0.0-rc1 + +Released 2020-11-25. + +### Added + +* The `Arbitrary` trait is now implemented for `&str`. [#63](https://github.com/rust-fuzz/arbitrary/pull/63) + +### Changed + +* The `Arbitrary` trait now has a lifetime parameter, allowing `Arbitrary` implementations that borrow from the raw input (e.g. the new `&str` implementaton). The `derive(Arbitrary)` macro also supports deriving `Arbitrary` on types with lifetimes now. [#63](https://github.com/rust-fuzz/arbitrary/pull/63) + +### Removed + +* The `shrink` method on the `Arbitrary` trait has been removed. + + We have found that, in practice, using [internal reduction](https://drmaciver.github.io/papers/reduction-via-generation-preview.pdf) via approaches like `cargo fuzz tmin`, where the raw input bytes are reduced rather than the `T: Arbitrary` type constructed from those raw bytes, has the best efficiency-to-maintenance ratio. To the best of our knowledge, no one is relying on or using the `Arbitrary::shrink` method. If you *are* using and relying on the `Arbitrary::shrink` method, please reach out by [dropping a comment here](https://github.com/rust-fuzz/arbitrary/issues/62) and explaining how you're using it and what your use case is. We'll figure out what the best solution is, including potentially adding shrinking functionality back to the `arbitrary` crate. + +-------------------------------------------------------------------------------- + ## 0.4.7 Released 2020-10-14. diff --git a/Cargo.toml b/Cargo.toml index 86a1577..ed72b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,26 @@ [package] name = "arbitrary" -version = "0.4.7" # Make sure this matches the derive crate version -authors = ["The Rust-Fuzz Project Developers", "Nick Fitzgerald ", "Manish Goregaokar ", "Simonas Kazlauskas ", "Brian L. Troutwine "] +version = "1.2.3" # Make sure this matches the derive crate version (not including the patch version) +authors = [ + "The Rust-Fuzz Project Developers", + "Nick Fitzgerald ", + "Manish Goregaokar ", + "Simonas Kazlauskas ", + "Brian L. Troutwine ", + "Corey Farwell ", +] categories = ["development-tools::testing"] edition = "2018" keywords = ["arbitrary", "testing"] readme = "README.md" description = "The trait for generating structured data from unstructured data" -license = "MIT/Apache-2.0" -repository = "https://github.com/nagisa/rust_arbitrary/" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-fuzz/arbitrary/" documentation = "https://docs.rs/arbitrary/" +rust-version = "1.63.0" [dependencies] -derive_arbitrary = { version = "=0.4.7", path = "./derive", optional = true } - -[dev-dependencies] +derive_arbitrary = { version = "1.2.3", path = "./derive", optional = true } [features] # Turn this feature on to enable support for `#[derive(Arbitrary)]`. @@ -30,3 +36,4 @@ path = "./tests/derive.rs" required-features = ["derive"] [workspace] +members = ["./fuzz"] diff --git a/README.md b/README.md index 7c62e6f..3f5619b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## About -The `Arbitrary` crate lets you construct arbitrary instance of a type. +The `Arbitrary` crate lets you construct arbitrary instances of a type. This crate is primarily intended to be combined with a fuzzer like [libFuzzer and `cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) or @@ -43,7 +43,7 @@ feature: # Cargo.toml [dependencies] -arbitrary = { version = "0.4", features = ["derive"] } +arbitrary = { version = "1", features = ["derive"] } ``` And then you can simply add `#[derive(Arbitrary)]` annotations to your types: @@ -61,6 +61,39 @@ pub struct Rgb { } ``` +#### Customizing single fields + +This can be particular handy if your structure uses a type that does not implement `Arbitrary` or you want to have more customization for particular fields. + +```rust +#[derive(Arbitrary)] +pub struct Rgba { + // set `r` to Default::default() + #[arbitrary(default)] + pub r: u8, + + // set `g` to 255 + #[arbitrary(value = 255)] + pub g: u8, + + // Generate `b` with a custom function of type + // + // fn(&mut Unstructured) -> arbitrary::Result + // + // where `T` is the field's type. + #[arbitrary(with = arbitrary_b)] + pub b: u8, + + // Generate `a` with a custom closure (shortuct to avoid a custom funciton) + #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=64))] + pub a: u8, +} + +fn arbitrary_b(u: &mut Unstructured) -> arbitrary::Result { + u.int_in_range(64..=128) +} +``` + ### Implementing `Arbitrary` By Hand Alternatively, you can write an `Arbitrary` implementation by hand: @@ -77,8 +110,8 @@ pub struct Rgb { pub b: u8, } -impl Arbitrary for Rgb { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for Rgb { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { let r = u8::arbitrary(u)?; let g = u8::arbitrary(u)?; let b = u8::arbitrary(u)?; @@ -87,33 +120,6 @@ impl Arbitrary for Rgb { } ``` -### Shrinking - -To assist with test case reduction, where you want to find the smallest and most -easily understandable test case that still demonstrates a bug you've discovered, -the `Arbitrary` trait has a `shrink` method. The `shrink` method returns an -iterator of "smaller" instances of `self`. The provided, default implementation -returns an empty iterator. - -We can override the default for our `Rgb` struct above by shrinking each of its -components and then gluing them back together again: - -```rust -impl Arbitrary for Rgb { - // ... - - fn shrink(&self) -> Box> { - let rs = self.r.shrink(); - let gs = self.g.shrink(); - let bs = self.b.shrink(); - Box::new(rs.zip(gs).zip(bs).map(|((r, g), b)| Rgb { r, g, b })) - } -} -``` - -Note that deriving `Arbitrary` will automatically derive a custom `shrink` -implementation for you. - ## License Licensed under dual MIT or Apache-2.0 at your choice. diff --git a/derive/Cargo.toml b/derive/Cargo.toml index f084540..99f5538 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -1,15 +1,22 @@ [package] name = "derive_arbitrary" -version = "0.4.7" # Make sure it matches the version of the arbitrary crate itself. -authors = ["The Rust-Fuzz Project Developers", "Nick Fitzgerald ", "Manish Goregaokar ", "Andre Bogus "] +version = "1.2.3" # Make sure it matches the version of the arbitrary crate itself (not including the patch version) +authors = [ + "The Rust-Fuzz Project Developers", + "Nick Fitzgerald ", + "Manish Goregaokar ", + "Andre Bogus ", + "Corey Farwell ", +] categories = ["development-tools::testing"] -edition = "2018" +edition = "2021" keywords = ["arbitrary", "testing", "derive", "macro"] readme = "README.md" description = "Derives arbitrary traits" license = "MIT/Apache-2.0" -repository = "https://github.com/nagisa/rust_arbitrary" +repository = "https://github.com/rust-fuzz/arbitrary" documentation = "https://docs.rs/arbitrary/" +rust-version = "1.63.0" [dependencies] proc-macro2 = "1.0" diff --git a/derive/src/field_attributes.rs b/derive/src/field_attributes.rs new file mode 100644 index 0000000..ccaba74 --- /dev/null +++ b/derive/src/field_attributes.rs @@ -0,0 +1,117 @@ +use proc_macro2::{Group, Span, TokenStream, TokenTree}; +use quote::quote; +use syn::{spanned::Spanned, *}; + +/// Used to filter out necessary field attribute and within error messages. +static ARBITRARY_ATTRIBUTE_NAME: &str = "arbitrary"; + +/// Determines how a value for a field should be constructed. +#[cfg_attr(test, derive(Debug))] +pub enum FieldConstructor { + /// Assume that Arbitrary is defined for the type of this field and use it (default) + Arbitrary, + + /// Places `Default::default()` as a field value. + Default, + + /// Use custom function or closure to generate a value for a field. + With(TokenStream), + + /// Set a field always to the given value. + Value(TokenStream), +} + +pub fn determine_field_constructor(field: &Field) -> Result { + let opt_attr = fetch_attr_from_field(field)?; + let ctor = match opt_attr { + Some(attr) => parse_attribute(attr)?, + None => FieldConstructor::Arbitrary, + }; + Ok(ctor) +} + +fn fetch_attr_from_field(field: &Field) -> Result> { + let found_attributes: Vec<_> = field + .attrs + .iter() + .filter(|a| { + let path = &a.path; + let name = quote!(#path).to_string(); + name == ARBITRARY_ATTRIBUTE_NAME + }) + .collect(); + if found_attributes.len() > 1 { + let name = field.ident.as_ref().unwrap(); + let msg = format!( + "Multiple conflicting #[{ARBITRARY_ATTRIBUTE_NAME}] attributes found on field `{name}`" + ); + return Err(syn::Error::new(field.span(), msg)); + } + Ok(found_attributes.into_iter().next()) +} + +fn parse_attribute(attr: &Attribute) -> Result { + let group = { + let mut tokens_iter = attr.clone().tokens.into_iter(); + let token = tokens_iter.next().ok_or_else(|| { + let msg = format!("#[{ARBITRARY_ATTRIBUTE_NAME}] cannot be empty."); + syn::Error::new(attr.span(), msg) + })?; + match token { + TokenTree::Group(g) => g, + t => { + let msg = format!("#[{ARBITRARY_ATTRIBUTE_NAME}] must contain a group, got: {t})"); + return Err(syn::Error::new(attr.span(), msg)); + } + } + }; + parse_attribute_internals(group) +} + +fn parse_attribute_internals(group: Group) -> Result { + let stream = group.stream(); + let mut tokens_iter = stream.into_iter(); + let token = tokens_iter.next().ok_or_else(|| { + let msg = format!("#[{ARBITRARY_ATTRIBUTE_NAME}] cannot be empty."); + syn::Error::new(group.span(), msg) + })?; + match token.to_string().as_ref() { + "default" => Ok(FieldConstructor::Default), + "with" => { + let func_path = parse_assigned_value("with", tokens_iter, group.span())?; + Ok(FieldConstructor::With(func_path)) + } + "value" => { + let value = parse_assigned_value("value", tokens_iter, group.span())?; + Ok(FieldConstructor::Value(value)) + } + _ => { + let msg = format!("Unknown option for #[{ARBITRARY_ATTRIBUTE_NAME}]: `{token}`"); + Err(syn::Error::new(token.span(), msg)) + } + } +} + +// Input: +// = 2 + 2 +// Output: +// 2 + 2 +fn parse_assigned_value( + opt_name: &str, + mut tokens_iter: impl Iterator, + default_span: Span, +) -> Result { + let eq_sign = tokens_iter.next().ok_or_else(|| { + let msg = format!( + "Invalid syntax for #[{ARBITRARY_ATTRIBUTE_NAME}], `{opt_name}` is missing assignment." + ); + syn::Error::new(default_span, msg) + })?; + + if eq_sign.to_string() == "=" { + Ok(tokens_iter.collect()) + } else { + let msg = format!("Invalid syntax for #[{ARBITRARY_ATTRIBUTE_NAME}], expected `=` after `{opt_name}`, got: `{eq_sign}`"); + Err(syn::Error::new(eq_sign.span(), msg)) + } +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index e16212d..4ed3817 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,76 +1,190 @@ extern crate proc_macro; -use proc_macro2::{Literal, TokenStream}; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::*; -#[proc_macro_derive(Arbitrary)] +mod field_attributes; +use field_attributes::{determine_field_constructor, FieldConstructor}; + +static ARBITRARY_LIFETIME_NAME: &str = "'arbitrary"; + +#[proc_macro_derive(Arbitrary, attributes(arbitrary))] pub fn derive_arbitrary(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(tokens as syn::DeriveInput); + expand_derive_arbitrary(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} - let arbitrary_method = gen_arbitrary_method(&input); - let size_hint_method = gen_size_hint_method(&input); - let shrink_method = gen_shrink_method(&input); +fn expand_derive_arbitrary(input: syn::DeriveInput) -> Result { + let (lifetime_without_bounds, lifetime_with_bounds) = + build_arbitrary_lifetime(input.generics.clone()); + + let recursive_count = syn::Ident::new( + &format!("RECURSIVE_COUNT_{}", input.ident), + Span::call_site(), + ); + + let arbitrary_method = + gen_arbitrary_method(&input, lifetime_without_bounds.clone(), &recursive_count)?; + let size_hint_method = gen_size_hint_method(&input)?; let name = input.ident; // Add a bound `T: Arbitrary` to every type parameter T. - let generics = add_trait_bounds(input.generics); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - (quote! { - impl #impl_generics arbitrary::Arbitrary for #name #ty_generics #where_clause { - #arbitrary_method - #size_hint_method - #shrink_method - } + let generics = add_trait_bounds(input.generics, lifetime_without_bounds.clone()); + + // Build ImplGeneric with a lifetime (https://github.com/dtolnay/syn/issues/90) + let mut generics_with_lifetime = generics.clone(); + generics_with_lifetime + .params + .push(GenericParam::Lifetime(lifetime_with_bounds)); + let (impl_generics, _, _) = generics_with_lifetime.split_for_impl(); + + // Build TypeGenerics and WhereClause without a lifetime + let (_, ty_generics, where_clause) = generics.split_for_impl(); + + Ok(quote! { + const _: () = { + std::thread_local! { + #[allow(non_upper_case_globals)] + static #recursive_count: std::cell::Cell = std::cell::Cell::new(0); + } + + #[automatically_derived] + impl #impl_generics arbitrary::Arbitrary<#lifetime_without_bounds> for #name #ty_generics #where_clause { + #arbitrary_method + #size_hint_method + } + }; }) - .into() +} + +// Returns: (lifetime without bounds, lifetime with bounds) +// Example: ("'arbitrary", "'arbitrary: 'a + 'b") +fn build_arbitrary_lifetime(generics: Generics) -> (LifetimeDef, LifetimeDef) { + let lifetime_without_bounds = + LifetimeDef::new(Lifetime::new(ARBITRARY_LIFETIME_NAME, Span::call_site())); + let mut lifetime_with_bounds = lifetime_without_bounds.clone(); + + for param in generics.params.iter() { + if let GenericParam::Lifetime(lifetime_def) = param { + lifetime_with_bounds + .bounds + .push(lifetime_def.lifetime.clone()); + } + } + + (lifetime_without_bounds, lifetime_with_bounds) } // Add a bound `T: Arbitrary` to every type parameter T. -fn add_trait_bounds(mut generics: Generics) -> Generics { +fn add_trait_bounds(mut generics: Generics, lifetime: LifetimeDef) -> Generics { for param in generics.params.iter_mut() { if let GenericParam::Type(type_param) = param { - type_param.bounds.push(parse_quote!(arbitrary::Arbitrary)); + type_param + .bounds + .push(parse_quote!(arbitrary::Arbitrary<#lifetime>)); } } generics } -fn gen_arbitrary_method(input: &DeriveInput) -> TokenStream { - let ident = &input.ident; - let arbitrary_structlike = |fields| { - let arbitrary = construct(fields, |_, _| quote!(arbitrary::Arbitrary::arbitrary(u)?)); - let arbitrary_take_rest = construct_take_rest(fields); - quote! { - fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { - Ok(#ident #arbitrary) +fn with_recursive_count_guard( + recursive_count: &syn::Ident, + expr: impl quote::ToTokens, +) -> impl quote::ToTokens { + quote! { + let guard_against_recursion = u.is_empty(); + if guard_against_recursion { + #recursive_count.with(|count| { + if count.get() > 0 { + return Err(arbitrary::Error::NotEnoughData); + } + count.set(count.get() + 1); + Ok(()) + })?; + } + + let result = (|| { #expr })(); + + if guard_against_recursion { + #recursive_count.with(|count| { + count.set(count.get() - 1); + }); + } + + result + } +} + +fn gen_arbitrary_method( + input: &DeriveInput, + lifetime: LifetimeDef, + recursive_count: &syn::Ident, +) -> Result { + fn arbitrary_structlike( + fields: &Fields, + ident: &syn::Ident, + lifetime: LifetimeDef, + recursive_count: &syn::Ident, + ) -> Result { + let arbitrary = construct(fields, |_idx, field| gen_constructor_for_field(field))?; + let body = with_recursive_count_guard(recursive_count, quote! { Ok(#ident #arbitrary) }); + + let arbitrary_take_rest = construct_take_rest(fields)?; + let take_rest_body = + with_recursive_count_guard(recursive_count, quote! { Ok(#ident #arbitrary_take_rest) }); + + Ok(quote! { + fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result { + #body } - fn arbitrary_take_rest(mut u: arbitrary::Unstructured<'_>) -> arbitrary::Result { - Ok(#ident #arbitrary_take_rest) + fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result { + #take_rest_body } - } - }; - match &input.data { - Data::Struct(data) => arbitrary_structlike(&data.fields), - Data::Union(data) => arbitrary_structlike(&Fields::Named(data.fields.clone())), + }) + } + + let ident = &input.ident; + let output = 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 = data.variants.iter().enumerate().map(|(i, variant)| { - let idx = i as u64; - let ctor = construct(&variant.fields, |_, _| { - quote!(arbitrary::Arbitrary::arbitrary(u)?) - }); - let variant_name = &variant.ident; - quote! { #idx => #ident::#variant_name #ctor } - }); - let variants_take_rest = data.variants.iter().enumerate().map(|(i, variant)| { - let idx = i as u64; - let ctor = construct_take_rest(&variant.fields); - let variant_name = &variant.ident; - quote! { #idx => #ident::#variant_name #ctor } - }); + let variants: Vec = 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::>()?; + + let variants_take_rest: Vec = 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::>()?; + let count = data.variants.len() as u64; - quote! { - fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + + 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 @@ -78,9 +192,12 @@ fn gen_arbitrary_method(input: &DeriveInput) -> TokenStream { #(#variants,)* _ => unreachable!() }) - } + }, + ); - fn arbitrary_take_rest(mut u: arbitrary::Unstructured<'_>) -> arbitrary::Result { + 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 @@ -88,161 +205,144 @@ fn gen_arbitrary_method(input: &DeriveInput) -> TokenStream { #(#variants_take_rest,)* _ => unreachable!() }) + }, + ); + + quote! { + fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result { + #arbitrary + } + + fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result { + #arbitrary_take_rest } } } - } + }; + Ok(output) } -fn construct(fields: &Fields, ctor: impl Fn(usize, &Field) -> TokenStream) -> TokenStream { - match fields { +fn construct( + fields: &Fields, + ctor: impl Fn(usize, &Field) -> Result, +) -> Result { + let output = match fields { Fields::Named(names) => { - let names = names.named.iter().enumerate().map(|(i, f)| { - let name = f.ident.as_ref().unwrap(); - let ctor = ctor(i, f); - quote! { #name: #ctor } - }); + let names: Vec = names + .named + .iter() + .enumerate() + .map(|(i, f)| { + let name = f.ident.as_ref().unwrap(); + ctor(i, f).map(|ctor| quote! { #name: #ctor }) + }) + .collect::>()?; quote! { { #(#names,)* } } } Fields::Unnamed(names) => { - let names = names.unnamed.iter().enumerate().map(|(i, f)| { - let ctor = ctor(i, f); - quote! { #ctor } - }); + let names: Vec = names + .unnamed + .iter() + .enumerate() + .map(|(i, f)| ctor(i, f).map(|ctor| quote! { #ctor })) + .collect::>()?; quote! { ( #(#names),* ) } } Fields::Unit => quote!(), - } + }; + Ok(output) } -fn construct_take_rest(fields: &Fields) -> TokenStream { - construct(fields, |idx, _| { - if idx + 1 == fields.len() { - quote! { arbitrary::Arbitrary::arbitrary_take_rest(u)? } - } else { - quote! { arbitrary::Arbitrary::arbitrary(&mut u)? } - } +fn construct_take_rest(fields: &Fields) -> Result { + construct(fields, |idx, field| { + determine_field_constructor(field).map(|field_constructor| match field_constructor { + FieldConstructor::Default => quote!(Default::default()), + FieldConstructor::Arbitrary => { + if idx + 1 == fields.len() { + quote! { arbitrary::Arbitrary::arbitrary_take_rest(u)? } + } else { + quote! { arbitrary::Arbitrary::arbitrary(&mut u)? } + } + } + FieldConstructor::With(function_or_closure) => quote!((#function_or_closure)(&mut u)?), + FieldConstructor::Value(value) => quote!(#value), + }) }) } -fn gen_size_hint_method(input: &DeriveInput) -> TokenStream { +fn gen_size_hint_method(input: &DeriveInput) -> Result { let size_hint_fields = |fields: &Fields| { - let tys = fields.iter().map(|f| &f.ty); - quote! { - arbitrary::size_hint::and_all(&[ - #( <#tys as arbitrary::Arbitrary>::size_hint(depth) ),* - ]) - } + fields + .iter() + .map(|f| { + let ty = &f.ty; + determine_field_constructor(f).map(|field_constructor| { + match field_constructor { + FieldConstructor::Default | FieldConstructor::Value(_) => { + quote!((0, Some(0))) + } + FieldConstructor::Arbitrary => { + quote! { <#ty as arbitrary::Arbitrary>::size_hint(depth) } + } + + // Note that in this case it's hard to determine what size_hint must be, so size_of::() 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) } + } + } + }) + }) + .collect::>>() + .map(|hints| { + quote! { + arbitrary::size_hint::and_all(&[ + #( #hints ),* + ]) + } + }) }; let size_hint_structlike = |fields: &Fields| { - let hint = size_hint_fields(fields); - quote! { - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - arbitrary::size_hint::recursion_guard(depth, |depth| #hint) + size_hint_fields(fields).map(|hint| { + quote! { + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + arbitrary::size_hint::recursion_guard(depth, |depth| #hint) + } } - } + }) }; match &input.data { Data::Struct(data) => size_hint_structlike(&data.fields), Data::Union(data) => size_hint_structlike(&Fields::Named(data.fields.clone())), - Data::Enum(data) => { - let variants = data.variants.iter().map(|v| size_hint_fields(&v.fields)); - quote! { - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - arbitrary::size_hint::and( - ::size_hint(depth), - arbitrary::size_hint::recursion_guard(depth, |depth| { - arbitrary::size_hint::or_all(&[ #( #variants ),* ]) - }), - ) + Data::Enum(data) => data + .variants + .iter() + .map(|v| size_hint_fields(&v.fields)) + .collect::>>() + .map(|variants| { + quote! { + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + arbitrary::size_hint::and( + ::size_hint(depth), + arbitrary::size_hint::recursion_guard(depth, |depth| { + arbitrary::size_hint::or_all(&[ #( #variants ),* ]) + }), + ) + } } - } - } + }), } } -fn gen_shrink_method(input: &DeriveInput) -> TokenStream { - let ident = &input.ident; - let shrink_structlike = |fields| { - let inner = shrink("e!(#ident), fields, |i, field| match &field.ident { - Some(i) => quote!(&self.#i), - None => { - let i = Literal::usize_unsuffixed(i); - quote!(&self.#i) - } - }); - quote! { - fn shrink(&self) -> Box> { - #inner - } - } +fn gen_constructor_for_field(field: &Field) -> Result { + let ctor = match determine_field_constructor(field)? { + FieldConstructor::Default => quote!(Default::default()), + FieldConstructor::Arbitrary => quote!(arbitrary::Arbitrary::arbitrary(u)?), + FieldConstructor::With(function_or_closure) => quote!((#function_or_closure)(u)?), + FieldConstructor::Value(value) => quote!(#value), }; - - return match &input.data { - Data::Struct(data) => shrink_structlike(&data.fields), - Data::Union(data) => shrink_structlike(&Fields::Named(data.fields.clone())), - Data::Enum(data) => { - let variants = data.variants.iter().map(|variant| { - let mut binding_names = Vec::new(); - let bindings = match &variant.fields { - Fields::Named(_) => { - let names = variant.fields.iter().map(|f| { - let name = f.ident.as_ref().unwrap(); - binding_names.push(quote!(#name)); - name - }); - quote!({#(#names),*}) - } - Fields::Unnamed(_) => { - let names = (0..variant.fields.len()).map(|i| { - let name = quote::format_ident!("f{}", i); - binding_names.push(quote!(#name)); - name - }); - quote!((#(#names),*)) - } - Fields::Unit => quote!(), - }; - let variant_name = &variant.ident; - let shrink = shrink("e!(#ident::#variant_name), &variant.fields, |i, _| { - binding_names[i].clone() - }); - quote!(#ident::#variant_name #bindings => { #shrink }) - }); - quote! { - fn shrink(&self) -> Box> { - match self { - #(#variants)* - } - } - } - } - }; - - fn shrink( - prefix: &TokenStream, - fields: &Fields, - access_field: impl Fn(usize, &Field) -> TokenStream, - ) -> TokenStream { - if fields.len() == 0 { - return quote!(Box::new(None.into_iter())); - } - let iters = fields.iter().enumerate().map(|(i, f)| { - let name = quote::format_ident!("field{}", i); - let field = access_field(i, f); - quote! { let mut #name = arbitrary::Arbitrary::shrink(#field); } - }); - let ctor = construct(fields, |i, _| { - let iter = quote::format_ident!("field{}", i); - quote!(#iter.next()?) - }); - quote! { - #(#iters)* - Box::new(std::iter::from_fn(move || { - Some(#prefix #ctor) - })) - } - } + Ok(ctor) } diff --git a/examples/derive_enum.rs b/examples/derive_enum.rs index fbcc106..c4fc9c9 100644 --- a/examples/derive_enum.rs +++ b/examples/derive_enum.rs @@ -2,6 +2,10 @@ //! //! Note that this requires enabling the "derive" cargo feature. +// Various enums/fields that we are deriving `Arbitrary` for aren't actually +// used except to show off the derive. +#![allow(dead_code)] + use arbitrary::{Arbitrary, Unstructured}; #[derive(Arbitrary, Debug)] diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..a092511 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,3 @@ +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..91893b9 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "arbitrary-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.arbitrary] +path = ".." + +[[bin]] +name = "int_in_range" +path = "fuzz_targets/int_in_range.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/int_in_range.rs b/fuzz/fuzz_targets/int_in_range.rs new file mode 100755 index 0000000..14c07f0 --- /dev/null +++ b/fuzz/fuzz_targets/int_in_range.rs @@ -0,0 +1,49 @@ +#![no_main] + +use arbitrary::{unstructured::Int, Arbitrary, Result, Unstructured}; +use libfuzzer_sys::fuzz_target; +use std::{fmt::Display, ops::RangeInclusive}; + +fuzz_target!(|data: &[u8]| { + fuzz(data).expect("`int_in_range` should never return an error"); +}); + +fn fuzz(data: &[u8]) -> Result<()> { + let mut u = Unstructured::new(data); + + let choices = [ + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + assert_in_range::, + ]; + + let f = u.choose(&choices[..])?; + f(&mut u) +} + +fn assert_in_range<'a, 'b, T>(u: &'a mut Unstructured<'b>) -> Result<()> +where + T: Arbitrary<'b> + Int + Display, +{ + let range = RangeInclusive::::arbitrary(u)?; + let start = *range.start(); + let end = *range.end(); + + let x = u.int_in_range(range)?; + + if start <= x && x <= end { + return Ok(()); + } + + let ty = std::any::type_name::(); + panic!("{ty}: {x} is not within {start}..={end}",); +} diff --git a/src/error.rs b/src/error.rs index 8cbd076..1d3df2a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use std::{error, fmt}; #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum Error { + /// No choices were provided to the Unstructured::choose call + EmptyChoose, /// There was not enough underlying data to fulfill some request for raw /// bytes. NotEnoughData, @@ -14,6 +16,10 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Error::EmptyChoose => write!( + f, + "`arbitrary::Unstructured::choose` must be given a non-empty set of choices" + ), Error::NotEnoughData => write!( f, "There is not enough underlying raw data to construct an `Arbitrary` instance" @@ -31,4 +37,17 @@ impl error::Error for Error {} /// A `Result` with the error type fixed as `arbitrary::Error`. /// /// Either an `Ok(T)` or `Err(arbitrary::Error)`. -pub type Result = std::result::Result; +pub type Result = std::result::Result; + +#[cfg(test)] +mod tests { + // Often people will import our custom `Result` type because 99.9% of + // results in a file will be `arbitrary::Result` but then have that one last + // 0.1% that want to have a custom error type. Don't make them prefix that + // 0.1% as `std::result::Result`; instead, let `arbitrary::Result` have an + // overridable error type. + #[test] + fn can_use_custom_error_types_with_result() -> super::Result<(), String> { + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index f92850b..a3fa48b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,36 +34,31 @@ pub use unstructured::Unstructured; pub mod size_hint; +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, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize}; use std::sync::{Arc, Mutex}; -fn empty() -> Box> { - Box::new(iter::empty()) -} - -fn once(val: T) -> Box> { - Box::new(iter::once(val)) -} - /// Generate arbitrary structured values from raw, unstructured data. /// /// The `Arbitrary` trait allows you to generate valid structured values, like /// `HashMap`s, or ASTs, or `MyTomlConfig`, or any other data structure from -/// raw, unstructured bytes provided by a fuzzer. It also features built-in -/// shrinking, so that if you find a test case that triggers a bug, you can find -/// the smallest, most easiest-to-understand test case that still triggers that -/// bug. +/// raw, unstructured bytes provided by a fuzzer. /// /// # Deriving `Arbitrary` /// @@ -75,7 +70,7 @@ fn once(val: T) -> Box> { /// /// ```toml /// [dependencies] -/// arbitrary = { version = "0.4", features = ["derive"] } +/// arbitrary = { version = "1", features = ["derive"] } /// ``` /// /// Then, you add the `#[derive(Arbitrary)]` annotation to your `struct` or @@ -106,7 +101,7 @@ fn once(val: T) -> Box> { /// 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 you something of that sort. The +/// variably-sized collection type, or something of that sort. The /// [`Unstructured`][crate::Unstructured] type helps you with these tasks. /// /// ``` @@ -118,11 +113,11 @@ fn once(val: T) -> Box> { /// # } /// use arbitrary::{Arbitrary, Result, Unstructured}; /// -/// impl Arbitrary for MyCollection +/// impl<'a, T> Arbitrary<'a> for MyCollection /// where -/// T: Arbitrary, +/// T: Arbitrary<'a>, /// { -/// fn arbitrary(u: &mut Unstructured<'_>) -> Result { +/// fn arbitrary(u: &mut Unstructured<'a>) -> Result { /// // Get an iterator of arbitrary `T`s. /// let iter = u.arbitrary_iter::()?; /// @@ -138,21 +133,24 @@ fn once(val: T) -> Box> { /// } /// # } /// ``` -pub trait Arbitrary: Sized + 'static { +pub trait Arbitrary<'a>: Sized { /// Generate an arbitrary value of `Self` from the given unstructured data. /// /// Calling `Arbitrary::arbitrary` requires that you have some raw data, /// perhaps given to you by a fuzzer like AFL or libFuzzer. You wrap this /// raw data in an `Unstructured`, and then you can call `::arbitrary` to construct an arbitrary instance of `MyType` - /// from that unstuctured data. + /// from that unstructured data. /// - /// Implementation may return an error if there is not enough data to - /// construct a full instance of `Self`. This is generally OK: it is better - /// to exit early and get the fuzzer to provide more input data, than it is - /// to generate default values in place of the missing data, which would - /// bias the distribution of generated values, and ultimately make fuzzing - /// less efficient. + /// Implementations may return an error if there is not enough data to + /// construct a full instance of `Self`, or they may fill out the rest of + /// `Self` with dummy values. Using dummy values when the underlying data is + /// exhausted can help avoid accidentally "defeating" some of the fuzzer's + /// mutations to the underlying byte stream that might otherwise lead to + /// interesting runtime behavior or new code coverage if only we had just a + /// few more bytes. However, it also requires that implementations for + /// recursive types (e.g. `struct Foo(Option>)`) avoid infinite + /// recursion when the underlying data is exhausted. /// /// ``` /// # #[cfg(feature = "derive")] fn foo() { @@ -179,14 +177,16 @@ pub trait Arbitrary: Sized + 'static { /// ``` /// /// See also the documentation for [`Unstructured`][crate::Unstructured]. - fn arbitrary(u: &mut Unstructured<'_>) -> Result; + fn arbitrary(u: &mut Unstructured<'a>) -> Result; - /// Generate an arbitrary value of `Self` from the entirety of the given unstructured data. + /// Generate an arbitrary value of `Self` from the entirety of the given + /// unstructured data. /// - /// 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]. - fn arbitrary_take_rest(mut u: Unstructured<'_>) -> Result { + /// 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]. + fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result { Self::arbitrary(&mut u) } @@ -209,13 +209,24 @@ pub trait Arbitrary: Sized + 'static { /// default with a better implementation. The /// [`size_hint`][crate::size_hint] module will help with this task. /// + /// ## 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. + /// /// ## The `depth` Parameter /// /// 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. /// Note that if you are implementing `Arbitrary` for a generic type, you - /// cannot guarantee the lack of type recrusion! + /// cannot guarantee the lack of type recursion! /// /// Otherwise, you need to use /// [`arbitrary::size_hint::recursion_guard(depth)`][crate::size_hint::recursion_guard] @@ -232,10 +243,10 @@ pub trait Arbitrary: Sized + 'static { /// Right(R), /// } /// - /// impl Arbitrary for MyEither + /// impl<'a, L, R> Arbitrary<'a> for MyEither /// where - /// L: Arbitrary, - /// R: Arbitrary, + /// L: Arbitrary<'a>, + /// R: Arbitrary<'a>, /// { /// fn arbitrary(u: &mut Unstructured) -> arbitrary::Result { /// // ... @@ -267,29 +278,10 @@ pub trait Arbitrary: Sized + 'static { let _ = depth; (0, None) } - - /// Generate an iterator of derived values which are "smaller" than the - /// original `self` instance. - /// - /// You can use this to help find the smallest test case that reproduces a - /// bug. - /// - /// Using `#[derive(Arbitrary)]` will automatically implement shrinking for - /// your type. - /// - /// However, if you are implementing `Arbirary` by hand and you want support - /// for shrinking your type, you must override the default provided - /// implementation of `shrink`, which just returns an empty iterator. You - /// should try pretty hard to have your `shrink` implementation return a - /// *lazy* iterator: one that computes the next value as it is needed, - /// rather than computing them up front when `shrink` is first called. - fn shrink(&self) -> Box> { - empty() - } } -impl Arbitrary for () { - fn arbitrary(_: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for () { + fn arbitrary(_: &mut Unstructured<'a>) -> Result { Ok(()) } @@ -299,26 +291,22 @@ impl Arbitrary for () { } } -impl Arbitrary for bool { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { - Ok(::arbitrary(u)? & 1 == 1) +impl<'a> Arbitrary<'a> for bool { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(>::arbitrary(u)? & 1 == 1) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - Box::new(if *self { once(false) } else { empty() }) + >::size_hint(depth) } } macro_rules! impl_arbitrary_for_integers { ( $( $ty:ty: $unsigned:ty; )* ) => { $( - impl Arbitrary for $ty { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { + impl<'a> Arbitrary<'a> for $ty { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { let mut buf = [0; mem::size_of::<$ty>()]; u.fill_buffer(&mut buf)?; let mut x: $unsigned = 0; @@ -334,20 +322,6 @@ macro_rules! impl_arbitrary_for_integers { (n, Some(n)) } - fn shrink(&self) -> Box> { - let mut x = *self; - if x == 0 { - return empty(); - } - Box::new(iter::once(0).chain(std::iter::from_fn(move || { - x = x / 2; - if x == 0 { - None - } else { - Some(x) - } - }))) - } } )* } @@ -371,45 +345,14 @@ impl_arbitrary_for_integers! { macro_rules! impl_arbitrary_for_floats { ( $( $ty:ident : $unsigned:ty; )* ) => { $( - impl Arbitrary for $ty { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { - Ok(Self::from_bits(<$unsigned as Arbitrary>::arbitrary(u)?)) + impl<'a> Arbitrary<'a> for $ty { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(Self::from_bits(<$unsigned as Arbitrary<'a>>::arbitrary(u)?)) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - <$unsigned as Arbitrary>::size_hint(depth) - } - - fn shrink(&self) -> Box> { - if *self == 0.0 { - empty() - } else if !self.is_finite() { - once(0.0) - } else { - let mut x = *self; - Box::new(iter::once(0.0).chain(iter::from_fn(move || { - // NB: do not test for zero like we do for integers - // because dividing by two until we reach a fixed - // point is NOT guaranteed to end at zero in - // non-default rounding modes of IEEE-754! - // - // For example, with 64-bit floats and the - // round-to-positive-infinity mode: - // - // 5e-324 / 2.0 == 5e-324 - // - // (5e-234 is the smallest postive number that can - // be precisely represented in a 64-bit float.) - let y = x; - x = x / 2.0; - if x == y { - None - } else { - Some(x) - } - }))) - } + <$unsigned as Arbitrary<'a>>::size_hint(depth) } } )* @@ -421,15 +364,16 @@ impl_arbitrary_for_floats! { f64: u64; } -impl Arbitrary for char { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for char { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { use std::char; - const CHAR_END: u32 = 0x0011_000; + // 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 = ::arbitrary(u)? % CHAR_END; + let mut c = >::arbitrary(u)? % CHAR_END; if let Some(c) = char::from_u32(c) { - return Ok(c); + Ok(c) } else { // We found a surrogate, wrap and try again c -= SURROGATES_START; @@ -440,66 +384,40 @@ impl Arbitrary for char { #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - let x = *self as u32; - Box::new(x.shrink().filter_map(|x| { - use std::convert::TryFrom; - char::try_from(x).ok() - })) + >::size_hint(depth) } } -impl Arbitrary for AtomicBool { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for AtomicBool { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - if self.load(Ordering::SeqCst) { - once(AtomicBool::new(false)) - } else { - empty() - } + >::size_hint(depth) } } -impl Arbitrary for AtomicIsize { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for AtomicIsize { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - let x = self.load(Ordering::SeqCst); - Box::new(x.shrink().map(Self::new)) + >::size_hint(depth) } } -impl Arbitrary for AtomicUsize { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for AtomicUsize { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - let x = self.load(Ordering::SeqCst); - Box::new(x.shrink().map(Self::new)) + >::size_hint(depth) } } @@ -511,11 +429,11 @@ macro_rules! impl_range { $fun:ident($fun_closure:expr), $size_hint_closure:expr ) => { - impl Arbitrary for $range + impl<'a, A> Arbitrary<'a> for $range where - A: Arbitrary + Clone + PartialOrd, + A: Arbitrary<'a> + Clone + PartialOrd, { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { let value: $value_ty = Arbitrary::arbitrary(u)?; Ok($fun(value, $fun_closure)) } @@ -524,11 +442,6 @@ macro_rules! impl_range { fn size_hint(depth: usize) -> (usize, Option) { $size_hint_closure(depth) } - - fn shrink(&self) -> Box> { - let value: $value_ty = $value_closure(self); - Box::new(value.shrink().map(|v| $fun(v, $fun_closure))) - } } }; } @@ -575,7 +488,7 @@ impl_range!( |depth| ::size_hint(depth) ); -fn bounded_range(bounds: (I, I), cb: CB) -> R +pub(crate) fn bounded_range(bounds: (I, I), cb: CB) -> R where CB: Fn((I, I)) -> R, I: PartialOrd, @@ -588,7 +501,7 @@ where cb((start, end)) } -fn unbounded_range(bound: I, cb: CB) -> R +pub(crate) fn unbounded_range(bound: I, cb: CB) -> R where CB: Fn(I) -> R, R: RangeBounds, @@ -596,8 +509,8 @@ where cb(bound) } -impl Arbitrary for Duration { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for Duration { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Ok(Self::new( ::arbitrary(u)?, u.int_in_range(0..=999_999_999)?, @@ -611,19 +524,11 @@ impl Arbitrary for Duration { ::size_hint(depth), ) } - - fn shrink(&self) -> Box> { - Box::new( - (self.as_secs(), self.subsec_nanos()) - .shrink() - .map(|(secs, nanos)| Duration::new(secs, nanos % 1_000_000_000)), - ) - } } -impl Arbitrary for Option { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { - Ok(if ::arbitrary(u)? { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Option { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(if >::arbitrary(u)? { Some(Arbitrary::arbitrary(u)?) } else { None @@ -637,19 +542,11 @@ impl Arbitrary for Option { crate::size_hint::or((0, Some(0)), ::size_hint(depth)), ) } - - fn shrink(&self) -> Box> { - if let Some(ref a) = *self { - Box::new(iter::once(None).chain(a.shrink().map(Some))) - } else { - empty() - } - } } -impl Arbitrary for std::result::Result { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { - Ok(if ::arbitrary(u)? { +impl<'a, A: Arbitrary<'a>, B: Arbitrary<'a>> Arbitrary<'a> for std::result::Result { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(if >::arbitrary(u)? { Ok(::arbitrary(u)?) } else { Err(::arbitrary(u)?) @@ -666,13 +563,6 @@ impl Arbitrary for std::result::Result { ), ) } - - fn shrink(&self) -> Box> { - match *self { - Ok(ref a) => Box::new(a.shrink().map(Ok)), - Err(ref b) => Box::new(b.shrink().map(Err)), - } - } } macro_rules! arbitrary_tuple { @@ -680,13 +570,13 @@ macro_rules! arbitrary_tuple { ($last: ident $($xs: ident)*) => { arbitrary_tuple!($($xs)*); - impl<$($xs: Arbitrary,)* $last: Arbitrary> Arbitrary for ($($xs,)* $last,) { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { + impl<'a, $($xs: Arbitrary<'a>,)* $last: Arbitrary<'a>> Arbitrary<'a> for ($($xs,)* $last,) { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Ok(($($xs::arbitrary(u)?,)* Arbitrary::arbitrary(u)?,)) } #[allow(unused_mut, non_snake_case)] - fn arbitrary_take_rest(mut u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result { $(let $xs = $xs::arbitrary(&mut u)?;)* let $last = $last::arbitrary_take_rest(u)?; Ok(($($xs,)* $last,)) @@ -699,299 +589,240 @@ macro_rules! arbitrary_tuple { $( <$xs as Arbitrary>::size_hint(depth) ),* ]) } - - #[allow(non_snake_case)] - fn shrink(&self) -> Box> { - let ( $( $xs, )* $last, ) = self; - let ( $( mut $xs, )* mut $last,) = ( $( $xs.shrink(), )* $last.shrink(),); - Box::new(iter::from_fn(move || { - Some(( $( $xs.next()? ,)* $last.next()?, )) - })) - } } }; } 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); -macro_rules! arbitrary_array { - {$n:expr, ($t:ident, $a:ident) $(($ts:ident, $as:ident))*} => { - arbitrary_array!{($n - 1), $(($ts, $as))*} - - impl Arbitrary for [T; $n] { - fn arbitrary(u: &mut Unstructured<'_>) -> Result<[T; $n]> { - Ok([ - Arbitrary::arbitrary(u)?, - $(<$ts as Arbitrary>::arbitrary(u)?),* - ]) - } - - #[allow(unused_mut)] - fn arbitrary_take_rest(mut u: Unstructured<'_>) -> Result<[T; $n]> { - $(let $as = $ts::arbitrary(&mut u)?;)* - let last = Arbitrary::arbitrary_take_rest(u)?; - - Ok([ - $($as,)* last - ]) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and_all(&[ - <$t as Arbitrary>::size_hint(depth), - $( <$ts as Arbitrary>::size_hint(depth) ),* - ]) - } +// Helper to safely create arrays since the standard library doesn't +// provide one yet. Shouldn't be necessary in the future. +struct ArrayGuard { + dst: *mut T, + initialized: usize, +} - #[allow(unused_mut)] // For the `[T; 1]` case. - fn shrink(&self) -> Box> { - let mut i = 0; - let mut shrinkers = [ - self[i].shrink(), - $({ - i += 1; - let t: &$ts = &self[i]; - t.shrink() - }),* - ]; - Box::new(iter::from_fn(move || { - let mut i = 0; - Some([ - shrinkers[i].next()?, - $({ - i += 1; - let t: $ts = shrinkers[i].next()?; - t - }),* - ]) - })) - } +impl Drop for ArrayGuard { + 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); } - }; - ($n: expr,) => {}; + } } -impl Arbitrary for [T; 0] { - fn arbitrary(_: &mut Unstructured<'_>) -> Result<[T; 0]> { - Ok([]) +fn try_create_array(mut cb: F) -> Result<[T; N]> +where + F: FnMut(usize) -> Result, +{ + 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 = 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()) } +} - fn arbitrary_take_rest(_: Unstructured<'_>) -> Result<[T; 0]> { - Ok([]) +impl<'a, T, const N: usize> Arbitrary<'a> for [T; N] +where + T: Arbitrary<'a>, +{ + #[inline] + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + try_create_array(|_| >::arbitrary(u)) } #[inline] - fn size_hint(_: usize) -> (usize, Option) { - crate::size_hint::and_all(&[]) + fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result { + let mut array = Self::arbitrary(&mut u)?; + if let Some(last) = array.last_mut() { + *last = Arbitrary::arbitrary_take_rest(u)?; + } + Ok(array) } - fn shrink(&self) -> Box> { - Box::new(iter::from_fn(|| None)) + #[inline] + fn size_hint(d: usize) -> (usize, Option) { + crate::size_hint::and_all(&array::from_fn::<_, N, _>(|_| { + ::size_hint(d) + })) } } -arbitrary_array! { 32, (T, a) (T, b) (T, c) (T, d) (T, e) (T, f) (T, g) (T, h) -(T, i) (T, j) (T, k) (T, l) (T, m) (T, n) (T, o) (T, p) -(T, q) (T, r) (T, s) (T, u) (T, v) (T, w) (T, x) (T, y) -(T, z) (T, aa) (T, ab) (T, ac) (T, ad) (T, ae) (T, af) -(T, ag) } - -fn shrink_collection<'a, T, A: Arbitrary>( - entries: impl Iterator, - f: impl Fn(&T) -> Box>, -) -> Box>> { - let entries: Vec<_> = entries.collect(); - if entries.is_empty() { - return empty(); - } - - let mut shrinkers: Vec> = vec![]; - let mut i = entries.len(); - loop { - shrinkers.push(entries.iter().take(i).map(&f).collect()); - i = i / 2; - if i == 0 { - break; - } +impl<'a> Arbitrary<'a> for &'a [u8] { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let len = u.arbitrary_len::()?; + u.bytes(len) + } + + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { + Ok(u.take_rest()) + } + + #[inline] + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } - Box::new(iter::once(vec![]).chain(iter::from_fn(move || loop { - let mut shrinker = shrinkers.pop()?; - let x: Option> = shrinker.iter_mut().map(|s| s.next()).collect(); - if x.is_none() { - continue; - } - shrinkers.push(shrinker); - return x; - }))) } -impl Arbitrary for Vec { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Vec { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - shrink_collection(self.iter(), |x| x.shrink()) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for BTreeMap { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, K: Arbitrary<'a> + Ord, V: Arbitrary<'a>> Arbitrary<'a> for BTreeMap { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - let collections = - shrink_collection(self.iter(), |(k, v)| Box::new(k.shrink().zip(v.shrink()))); - Box::new(collections.map(|entries| entries.into_iter().collect())) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for BTreeSet { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BTreeSet { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } +} - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.iter(), |v| v.shrink()); - Box::new(collections.map(|entries| entries.into_iter().collect())) +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Bound { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + match u.int_in_range::(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) { + size_hint::or( + size_hint::and((1, Some(1)), A::size_hint(depth)), + (1, Some(1)), + ) } } -impl Arbitrary for BinaryHeap { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BinaryHeap { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.iter(), |v| v.shrink()); - Box::new(collections.map(|entries| entries.into_iter().collect())) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for HashMap { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>, S: BuildHasher + Default> + Arbitrary<'a> for HashMap +{ + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - let collections = - shrink_collection(self.iter(), |(k, v)| Box::new(k.shrink().zip(v.shrink()))); - Box::new(collections.map(|entries| entries.into_iter().collect())) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for HashSet { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a> + Eq + ::std::hash::Hash, S: BuildHasher + Default> Arbitrary<'a> + for HashSet +{ + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.iter(), |v| v.shrink()); - Box::new(collections.map(|entries| entries.into_iter().collect())) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for LinkedList { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for LinkedList { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.iter(), |v| v.shrink()); - Box::new(collections.map(|entries| entries.into_iter().collect())) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for VecDeque { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for VecDeque { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { u.arbitrary_iter()?.collect() } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { u.arbitrary_take_rest_iter()?.collect() } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) - } - - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.iter(), |v| v.shrink()); - Box::new(collections.map(|entries| entries.into_iter().collect())) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } } -impl Arbitrary for Cow<'static, A> +impl<'a, A> Arbitrary<'a> for Cow<'a, A> where A: ToOwned + ?Sized, - ::Owned: Arbitrary, + ::Owned: Arbitrary<'a>, { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Cow::Owned) } @@ -1001,55 +832,56 @@ where <::Owned as Arbitrary>::size_hint(depth) }) } - - fn shrink(&self) -> Box> { - match *self { - Cow::Owned(ref o) => Box::new(o.shrink().map(Cow::Owned)), - Cow::Borrowed(b) => Box::new(b.to_owned().shrink().map(Cow::Owned)), - } - } } -impl Arbitrary for String { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for &'a str { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { let size = u.arbitrary_len::()?; - match str::from_utf8(&u.peek_bytes(size).unwrap()) { + match str::from_utf8(u.peek_bytes(size).unwrap()) { Ok(s) => { - u.get_bytes(size).unwrap(); - Ok(s.into()) + u.bytes(size).unwrap(); + Ok(s) } Err(e) => { let i = e.valid_up_to(); - let valid = u.get_bytes(i).unwrap(); + let valid = u.bytes(i).unwrap(); let s = unsafe { debug_assert!(str::from_utf8(valid).is_ok()); str::from_utf8_unchecked(valid) }; - Ok(s.into()) + Ok(s) } } } - fn arbitrary_take_rest(u: Unstructured<'_>) -> Result { + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { let bytes = u.take_rest(); - str::from_utf8(bytes) - .map_err(|_| Error::IncorrectFormat) - .map(Into::into) + str::from_utf8(bytes).map_err(|_| Error::IncorrectFormat) } #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::and(::size_hint(depth), (0, None)) + fn size_hint(_depth: usize) -> (usize, Option) { + (0, None) } +} - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.chars(), |ch| ch.shrink()); - Box::new(collections.map(|chars| chars.into_iter().collect())) +impl<'a> Arbitrary<'a> for String { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + <&str as Arbitrary>::arbitrary(u).map(Into::into) + } + + fn arbitrary_take_rest(u: Unstructured<'a>) -> Result { + <&str as Arbitrary>::arbitrary_take_rest(u).map(Into::into) + } + + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + <&str as Arbitrary>::size_hint(depth) } } -impl Arbitrary for CString { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for CString { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { as Arbitrary>::arbitrary(u).map(|mut x| { x.retain(|&c| c != 0); Self::new(x).unwrap() @@ -1060,17 +892,10 @@ impl Arbitrary for CString { fn size_hint(depth: usize) -> (usize, Option) { as Arbitrary>::size_hint(depth) } - - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.as_bytes().iter(), |b| { - Box::new(b.shrink().filter(|&b| b != 0)) - }); - Box::new(collections.map(|bytes| Self::new(bytes).unwrap())) - } } -impl Arbitrary for OsString { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for OsString { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { ::arbitrary(u).map(From::from) } @@ -1078,18 +903,10 @@ impl Arbitrary for OsString { fn size_hint(depth: usize) -> (usize, Option) { ::size_hint(depth) } - - fn shrink(&self) -> Box> { - match self.clone().into_string() { - Err(_) if self.is_empty() => empty(), - Err(_) => once(OsString::from("".to_string())), - Ok(s) => Box::new(s.shrink().map(From::from)), - } - } } -impl Arbitrary for PathBuf { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for PathBuf { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { ::arbitrary(u).map(From::from) } @@ -1097,30 +914,21 @@ impl Arbitrary for PathBuf { fn size_hint(depth: usize) -> (usize, Option) { ::size_hint(depth) } - - fn shrink(&self) -> Box> { - let s = self.clone().into_os_string(); - Box::new(s.shrink().map(From::from)) - } } -impl Arbitrary for Box { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::recursion_guard(depth, |depth| ::size_hint(depth)) - } - - fn shrink(&self) -> Box> { - Box::new((&**self).shrink().map(Self::new)) + crate::size_hint::recursion_guard(depth, ::size_hint) } } -impl Arbitrary for Box<[A]> { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box<[A]> { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { as Arbitrary>::arbitrary(u).map(|x| x.into_boxed_slice()) } @@ -1128,14 +936,10 @@ impl Arbitrary for Box<[A]> { fn size_hint(depth: usize) -> (usize, Option) { as Arbitrary>::size_hint(depth) } - - fn shrink(&self) -> Box> { - Box::new(shrink_collection(self.iter(), |x| x.shrink()).map(|v| v.into_boxed_slice())) - } } -impl Arbitrary for Box { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a> Arbitrary<'a> for Box { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { ::arbitrary(u).map(|x| x.into_boxed_str()) } @@ -1143,11 +947,6 @@ impl Arbitrary for Box { fn size_hint(depth: usize) -> (usize, Option) { ::size_hint(depth) } - - fn shrink(&self) -> Box> { - let collections = shrink_collection(self.chars(), |ch| ch.shrink()); - Box::new(collections.map(|chars| chars.into_iter().collect::().into_boxed_str())) - } } // impl Arbitrary for Box { @@ -1163,101 +962,96 @@ impl Arbitrary for Box { // } // } -impl Arbitrary for Arc { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Arc { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::recursion_guard(depth, |depth| ::size_hint(depth)) + crate::size_hint::recursion_guard(depth, ::size_hint) } +} - fn shrink(&self) -> Box> { - Box::new((&**self).shrink().map(Self::new)) +impl<'a> Arbitrary<'a> for Arc { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + <&str as Arbitrary>::arbitrary(u).map(Into::into) + } + + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + <&str as Arbitrary>::size_hint(depth) } } -impl Arbitrary for Rc { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Rc { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - crate::size_hint::recursion_guard(depth, |depth| ::size_hint(depth)) + crate::size_hint::recursion_guard(depth, ::size_hint) + } +} + +impl<'a> Arbitrary<'a> for Rc { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + <&str as Arbitrary>::arbitrary(u).map(Into::into) } - fn shrink(&self) -> Box> { - Box::new((&**self).shrink().map(Self::new)) + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + <&str as Arbitrary>::size_hint(depth) } } -impl Arbitrary for Cell { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Cell { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) + >::size_hint(depth) } - - // Note: can't implement `shrink` without either more trait bounds on `A` - // (copy or default) or `Cell::update`: - // https://github.com/rust-lang/rust/issues/50186 } -impl Arbitrary for RefCell { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for RefCell { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - let x = self.borrow(); - Box::new(x.shrink().map(Self::new)) + >::size_hint(depth) } } -impl Arbitrary for UnsafeCell { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for UnsafeCell { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) + >::size_hint(depth) } - - // We can't non-trivially (i.e. not an empty iterator) implement `shrink` in - // a safe way, since we don't have a safe way to get the inner value. } -impl Arbitrary for Mutex { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Mutex { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(Self::new) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) - } - - fn shrink(&self) -> Box> { - match self.lock() { - Err(_) => empty(), - Ok(g) => Box::new(g.shrink().map(Self::new)), - } + >::size_hint(depth) } } -impl Arbitrary for iter::Empty { - fn arbitrary(_: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for iter::Empty { + fn arbitrary(_: &mut Unstructured<'a>) -> Result { Ok(iter::empty()) } @@ -1265,12 +1059,10 @@ impl Arbitrary for iter::Empty { fn size_hint(_depth: usize) -> (usize, Option) { (0, Some(0)) } - - // Nothing to shrink here. } -impl Arbitrary for ::std::marker::PhantomData { - fn arbitrary(_: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::marker::PhantomData { + fn arbitrary(_: &mut Unstructured<'a>) -> Result { Ok(::std::marker::PhantomData) } @@ -1278,23 +1070,69 @@ impl Arbitrary for ::std::marker::PhantomData { fn size_hint(_depth: usize) -> (usize, Option) { (0, Some(0)) } - - // Nothing to shrink here. } -impl Arbitrary for ::std::num::Wrapping { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { +impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::num::Wrapping { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { Arbitrary::arbitrary(u).map(::std::num::Wrapping) } #[inline] fn size_hint(depth: usize) -> (usize, Option) { - ::size_hint(depth) + >::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 { + match Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?) { + Some(n) => Ok(n), + None => Err(Error::IncorrectFormat), + } + } - fn shrink(&self) -> Box> { - let ref x = self.0; - Box::new(x.shrink().map(::std::num::Wrapping)) + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + <$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 { + Ok(Ipv4Addr::from(u32::arbitrary(u)?)) + } + + #[inline] + fn size_hint(_depth: usize) -> (usize, Option) { + (4, Some(4)) + } +} + +impl<'a> Arbitrary<'a> for Ipv6Addr { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(Ipv6Addr::from(u128::arbitrary(u)?)) + } + + #[inline] + fn size_hint(_depth: usize) -> (usize, Option) { + (16, Some(16)) } } @@ -1302,6 +1140,56 @@ impl Arbitrary for ::std::num::Wrapping { 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 { + 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 { + 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]; @@ -1320,26 +1208,48 @@ mod test { let x = [1, 2, 3, 4]; let mut buf = Unstructured::new(&x); let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24); - let actual = i32::arbitrary(&mut buf).unwrap(); + let actual = checked_arbitrary::(&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, + 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!( - Vec::::arbitrary(&mut Unstructured::new(&x)).unwrap(), + checked_arbitrary::<&[u8]>(&mut Unstructured::new(&x)).unwrap(), + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3] + ); + assert_eq!( + checked_arbitrary::>(&mut Unstructured::new(&x)).unwrap(), &[2, 4, 6, 8, 1] ); assert_eq!( - Vec::::arbitrary(&mut Unstructured::new(&x)).unwrap(), + checked_arbitrary::>(&mut Unstructured::new(&x)).unwrap(), &[84148994] ); assert_eq!( - String::arbitrary(&mut Unstructured::new(&x)).unwrap(), - "\x01\x02\x03\x04\x05\x06\x07\x08" + checked_arbitrary::(&mut Unstructured::new(&x)).unwrap(), + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03" ); } @@ -1347,130 +1257,86 @@ mod test { fn arbitrary_take_rest() { let x = [1, 2, 3, 4]; assert_eq!( - Vec::::arbitrary_take_rest(Unstructured::new(&x)).unwrap(), + checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&x)).unwrap(), &[1, 2, 3, 4] ); assert_eq!( - Vec::::arbitrary_take_rest(Unstructured::new(&x)).unwrap(), - &[0x4030201] - ); - assert_eq!( - String::arbitrary_take_rest(Unstructured::new(&x)).unwrap(), - "\x01\x02\x03\x04" - ); - } - - #[test] - fn shrink_tuple() { - let tup = (10, 20, 30); - assert_eq!( - tup.shrink().collect::>(), - [(0, 0, 0), (5, 10, 15), (2, 5, 7), (1, 2, 3)] + checked_arbitrary_take_rest::>(Unstructured::new(&x)).unwrap(), + &[1, 2, 3, 4] ); - } - - #[test] - fn shrink_array() { - let tup = [10, 20, 30]; assert_eq!( - tup.shrink().collect::>(), - [[0, 0, 0], [5, 10, 15], [2, 5, 7], [1, 2, 3]] + checked_arbitrary_take_rest::>(Unstructured::new(&x)).unwrap(), + &[0x4030201] ); - } - - #[test] - fn shrink_vec() { - let v = vec![4, 4, 4, 4]; assert_eq!( - v.shrink().collect::>(), - [ - vec![], - vec![0], - vec![2], - vec![1], - vec![0, 0], - vec![2, 2], - vec![1, 1], - vec![0, 0, 0, 0], - vec![2, 2, 2, 2], - vec![1, 1, 1, 1] - ] + checked_arbitrary_take_rest::(Unstructured::new(&x)).unwrap(), + "\x01\x02\x03\x04" ); - } - #[test] - fn shrink_string() { - let s = "aaaa".to_string(); assert_eq!( - s.shrink().collect::>(), - [ - "", - "\u{0}", - "0", - "\u{18}", - "\u{c}", - "\u{6}", - "\u{3}", - "\u{1}", - "\u{0}\u{0}", - "00", - "\u{18}\u{18}", - "\u{c}\u{c}", - "\u{6}\u{6}", - "\u{3}\u{3}", - "\u{1}\u{1}", - "\u{0}\u{0}\u{0}\u{0}", - "0000", - "\u{18}\u{18}\u{18}\u{18}", - "\u{c}\u{c}\u{c}\u{c}", - "\u{6}\u{6}\u{6}\u{6}", - "\u{3}\u{3}\u{3}\u{3}", - "\u{1}\u{1}\u{1}\u{1}" - ] - .iter() - .map(|s| s.to_string()) - .collect::>(), + checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&[])).unwrap(), + &[] ); - } - - #[test] - fn shrink_cstring() { - let s = CString::new(b"aaaa".to_vec()).unwrap(); assert_eq!( - s.shrink().collect::>(), - [ - &[][..], - &[b'0'][..], - &[0x18][..], - &[0x0c][..], - &[0x06][..], - &[0x03][..], - &[0x01][..], - &[b'0', b'0'][..], - &[0x18, 0x18][..], - &[0x0c, 0x0c][..], - &[0x06, 0x06][..], - &[0x03, 0x03][..], - &[0x01, 0x01][..], - &[b'0', b'0', b'0', b'0'][..], - &[0x18, 0x18, 0x18, 0x18][..], - &[0x0c, 0x0c, 0x0c, 0x0c][..], - &[0x06, 0x06, 0x06, 0x06][..], - &[0x03, 0x03, 0x03, 0x03][..], - &[0x01, 0x01, 0x01, 0x01][..], - ] - .iter() - .map(|s| CString::new(s.to_vec()).unwrap()) - .collect::>(), + checked_arbitrary_take_rest::>(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 + mem::size_of::(), None), - <(u8, Vec) as Arbitrary>::size_hint(0) + (7, Some(7)), + <(bool, u16, i32) as Arbitrary<'_>>::size_hint(0) ); + assert_eq!((1, None), <(u8, Vec) as Arbitrary>::size_hint(0)); } } + +/// Multiple conflicting arbitrary attributes are used on the same field: +/// ```compile_fail +/// #[derive(::arbitrary::Arbitrary)] +/// struct Point { +/// #[arbitrary(value = 2)] +/// #[arbitrary(value = 2)] +/// x: i32, +/// } +/// ``` +/// +/// An unknown attribute: +/// ```compile_fail +/// #[derive(::arbitrary::Arbitrary)] +/// struct Point { +/// #[arbitrary(unknown_attr)] +/// x: i32, +/// } +/// ``` +/// +/// An unknown attribute with a value: +/// ```compile_fail +/// #[derive(::arbitrary::Arbitrary)] +/// struct Point { +/// #[arbitrary(unknown_attr = 13)] +/// x: i32, +/// } +/// ``` +/// +/// `value` without RHS: +/// ```compile_fail +/// #[derive(::arbitrary::Arbitrary)] +/// struct Point { +/// #[arbitrary(value)] +/// x: i32, +/// } +/// ``` +/// +/// `with` without RHS: +/// ```compile_fail +/// #[derive(::arbitrary::Arbitrary)] +/// struct Point { +/// #[arbitrary(with)] +/// x: i32, +/// } +/// ``` +#[cfg(all(doctest, feature = "derive"))] +pub struct CompileFailTests; diff --git a/src/size_hint.rs b/src/size_hint.rs index e2aecc2..045c148 100644 --- a/src/size_hint.rs +++ b/src/size_hint.rs @@ -8,9 +8,6 @@ /// size hint. /// /// Otherwise, returns the default size hint: `(0, None)`. -/// -/// See the [docs for `Arbitrary::shrink`][crate::Arbitrary::shrink] for example -/// usage. #[inline] pub fn recursion_guard( depth: usize, diff --git a/src/unstructured.rs b/src/unstructured.rs index cffde0a..0bfdff2 100644 --- a/src/unstructured.rs +++ b/src/unstructured.rs @@ -10,6 +10,7 @@ use crate::{Arbitrary, Error, Result}; use std::marker::PhantomData; +use std::ops::ControlFlow; use std::{mem, ops}; /// A source of unstructured data. @@ -165,9 +166,9 @@ impl<'a> Unstructured<'a> { /// ``` pub fn arbitrary(&mut self) -> Result where - A: Arbitrary, + A: Arbitrary<'a>, { - ::arbitrary(self) + >::arbitrary(self) } /// Get the number of elements to insert when building up a collection of @@ -190,11 +191,11 @@ impl<'a> Unstructured<'a> { /// # pub fn insert(&mut self, element: T) {} /// # } /// - /// impl Arbitrary for MyCollection + /// impl<'a, T> Arbitrary<'a> for MyCollection /// where - /// T: Arbitrary, + /// T: Arbitrary<'a>, /// { - /// fn arbitrary(u: &mut Unstructured<'_>) -> Result { + /// fn arbitrary(u: &mut Unstructured<'a>) -> Result { /// // Get the number of `T`s we should insert into our collection. /// let len = u.arbitrary_len::()?; /// @@ -211,17 +212,17 @@ impl<'a> Unstructured<'a> { /// ``` pub fn arbitrary_len(&mut self) -> Result where - ElementType: Arbitrary, + ElementType: Arbitrary<'a>, { let byte_size = self.arbitrary_byte_size()?; let (lower, upper) = ::size_hint(0); - let elem_size = upper.unwrap_or_else(|| lower * 2); + let elem_size = upper.unwrap_or(lower * 2); let elem_size = std::cmp::max(1, elem_size); Ok(byte_size / elem_size) } fn arbitrary_byte_size(&mut self) -> Result { - if self.data.len() == 0 { + if self.data.is_empty() { Ok(0) } else if self.data.len() == 1 { self.data = &[]; @@ -235,19 +236,20 @@ impl<'a> Unstructured<'a> { // We only consume as many bytes as necessary to cover the entire // range of the byte string. - let len = if self.data.len() <= std::u8::MAX as usize + 1 { + // 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 { 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() <= std::u16::MAX as usize + 1 { + } else if self.data.len() as u64 <= std::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() <= std::u32::MAX as usize + 1 { + } else if self.data.len() as u64 <= std::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); @@ -272,7 +274,7 @@ impl<'a> Unstructured<'a> { /// /// # Panics /// - /// Panics if `range.start >= range.end`. That is, the given range must be + /// Panics if `range.start > range.end`. That is, the given range must be /// non-empty. /// /// # Example @@ -304,8 +306,8 @@ impl<'a> Unstructured<'a> { where T: Int, { - let start = range.start(); - let end = range.end(); + let start = *range.start(); + let end = *range.end(); assert!( start <= end, "`arbitrary::Unstructured::int_in_range` requires a non-empty range" @@ -314,30 +316,59 @@ impl<'a> Unstructured<'a> { // When there is only one possible choice, don't waste any entropy from // the underlying data. if start == end { - return Ok((*start, 0)); + return Ok((start, 0)); } - let range: T::Widest = end.as_widest() - start.as_widest(); - let mut result = T::Widest::ZERO; - let mut offset: usize = 0; + // From here on out we work with the unsigned representation. All of the + // operations performed below work out just as well whether or not `T` + // is a signed or unsigned integer. + let start = start.to_unsigned(); + let end = end.to_unsigned(); + + let delta = end.wrapping_sub(start); + debug_assert_ne!(delta, T::Unsigned::ZERO); + + // Compute an arbitrary integer offset from the start of the range. We + // do this by consuming `size_of(T)` bytes from the input to create an + // arbitrary integer and then clamping that int into our range bounds + // with a modulo operation. + let mut arbitrary_int = T::Unsigned::ZERO; + let mut bytes_consumed: usize = 0; - while offset < mem::size_of::() - && (range >> T::Widest::from_usize(offset)) > T::Widest::ZERO + while (bytes_consumed < mem::size_of::()) + && (delta >> T::Unsigned::from_usize(bytes_consumed * 8)) > T::Unsigned::ZERO { - let byte = bytes.next().ok_or(Error::NotEnoughData)?; - result = (result << 8) | T::Widest::from_u8(byte); - offset += 1; - } + let byte = match bytes.next() { + None => break, + Some(b) => b, + }; + bytes_consumed += 1; - // Avoid division by zero. - if let Some(range) = range.checked_add(T::Widest::ONE) { - result = result % range; + // Combine this byte into our arbitrary integer, but avoid + // overflowing the shift for `u8` and `i8`. + arbitrary_int = if mem::size_of::() == 1 { + T::Unsigned::from_u8(byte) + } else { + (arbitrary_int << 8) | T::Unsigned::from_u8(byte) + }; } - Ok(( - T::from_widest(start.as_widest().wrapping_add(result)), - offset, - )) + let offset = if delta == T::Unsigned::MAX { + arbitrary_int + } else { + arbitrary_int % (delta.checked_add(T::Unsigned::ONE).unwrap()) + }; + + // Finally, we add `start` to our offset from `start` to get the result + // actual value within the range. + let result = start.wrapping_add(offset); + + // And convert back to our maybe-signed representation. + let result = T::from_unsigned(result); + debug_assert!(*range.start() <= result); + debug_assert!(result <= *range.end()); + + Ok((result, bytes_consumed)) } /// Choose one of the given choices. @@ -345,33 +376,120 @@ impl<'a> Unstructured<'a> { /// This should only be used inside of `Arbitrary` implementations. /// /// Returns an error if there is not enough underlying data to make a - /// choice. + /// choice or if no choices are provided. /// - /// # Panics + /// # Examples /// - /// Panics if `choices` is empty. + /// Selecting from an array of choices: /// - /// # Example + /// ``` + /// use arbitrary::Unstructured; + /// + /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + /// let choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; + /// + /// let choice = u.choose(&choices).unwrap(); + /// + /// println!("chose {}", choice); + /// ``` + /// + /// An error is returned if no choices are provided: /// /// ``` /// use arbitrary::Unstructured; /// /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + /// let choices: [char; 0] = []; /// - /// let choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; - /// if let Ok(ch) = u.choose(&choices) { - /// println!("chose {}", ch); - /// } + /// let result = u.choose(&choices); + /// + /// assert!(result.is_err()); /// ``` pub fn choose<'b, T>(&mut self, choices: &'b [T]) -> Result<&'b T> { - assert!( - !choices.is_empty(), - "`arbitrary::Unstructured::choose` must be given a non-empty set of choices" - ); - let idx = self.int_in_range(0..=choices.len() - 1)?; + let idx = self.choose_index(choices.len())?; Ok(&choices[idx]) } + /// Choose a value in `0..len`. + /// + /// Returns an error if the `len` is zero. + /// + /// # Examples + /// + /// Using Fisher–Yates shuffle shuffle to gerate an arbitrary permutation. + /// + /// [Fisher–Yates shuffle]: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle + /// + /// ``` + /// use arbitrary::Unstructured; + /// + /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + /// let mut permutation = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; + /// let mut to_permute = &mut permutation[..]; + /// while to_permute.len() > 1 { + /// let idx = u.choose_index(to_permute.len()).unwrap(); + /// to_permute.swap(0, idx); + /// to_permute = &mut to_permute[1..]; + /// } + /// + /// println!("permutation: {:?}", permutation); + /// ``` + /// + /// An error is returned if the length is zero: + /// + /// ``` + /// use arbitrary::Unstructured; + /// + /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + /// let array: [i32; 0] = []; + /// + /// let result = u.choose_index(array.len()); + /// + /// assert!(result.is_err()); + /// ``` + pub fn choose_index(&mut self, len: usize) -> Result { + if len == 0 { + return Err(Error::EmptyChoose); + } + let idx = self.int_in_range(0..=len - 1)?; + Ok(idx) + } + + /// Generate a boolean according to the given ratio. + /// + /// # Panics + /// + /// Panics when the numerator and denominator do not meet these constraints: + /// + /// * `0 < numerator <= denominator` + /// + /// # Example + /// + /// Generate a boolean that is `true` five sevenths of the time: + /// + /// ``` + /// # fn foo() -> arbitrary::Result<()> { + /// use arbitrary::Unstructured; + /// + /// # let my_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + /// let mut u = Unstructured::new(&my_data); + /// + /// if u.ratio(5, 7)? { + /// // Take this branch 5/7 of the time. + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn ratio(&mut self, numerator: T, denominator: T) -> Result + where + T: Int, + { + assert!(T::ZERO < numerator); + assert!(numerator <= denominator); + let x = self.int_in_range(T::ONE..=denominator)?; + Ok(x <= numerator) + } + /// Fill a `buffer` with bytes from the underlying raw data. /// /// This should only be called within an `Arbitrary` implementation. This is @@ -379,8 +497,8 @@ impl<'a> Unstructured<'a> { /// `Arbitrary` implementations like `>::arbitrary` and /// `String::arbitrary` over using this method directly. /// - /// If this `Unstructured` does not have enough data to fill the whole - /// `buffer`, an error is returned. + /// If this `Unstructured` does not have enough underlying data to fill the + /// whole `buffer`, it pads the buffer out with zeros. /// /// # Example /// @@ -390,16 +508,21 @@ impl<'a> Unstructured<'a> { /// let mut u = Unstructured::new(&[1, 2, 3, 4]); /// /// let mut buf = [0; 2]; + /// /// assert!(u.fill_buffer(&mut buf).is_ok()); + /// assert_eq!(buf, [1, 2]); + /// + /// assert!(u.fill_buffer(&mut buf).is_ok()); + /// assert_eq!(buf, [3, 4]); + /// /// assert!(u.fill_buffer(&mut buf).is_ok()); + /// assert_eq!(buf, [0, 0]); /// ``` pub fn fill_buffer(&mut self, buffer: &mut [u8]) -> Result<()> { let n = std::cmp::min(buffer.len(), self.data.len()); - for i in 0..n { - buffer[i] = self.data[i]; - } - for i in self.data.len()..buffer.len() { - buffer[i] = 0; + buffer[..n].copy_from_slice(&self.data[..n]); + for byte in buffer[n..].iter_mut() { + *byte = 0; } self.data = &self.data[n..]; Ok(()) @@ -419,10 +542,10 @@ impl<'a> Unstructured<'a> { /// /// let mut u = Unstructured::new(&[1, 2, 3, 4]); /// - /// assert!(u.get_bytes(2).unwrap() == &[1, 2]); - /// assert!(u.get_bytes(2).unwrap() == &[3, 4]); + /// assert!(u.bytes(2).unwrap() == &[1, 2]); + /// assert!(u.bytes(2).unwrap() == &[3, 4]); /// ``` - pub fn get_bytes(&mut self, size: usize) -> Result<&'a [u8]> { + pub fn bytes(&mut self, size: usize) -> Result<&'a [u8]> { if self.data.len() < size { return Err(Error::NotEnoughData); } @@ -473,14 +596,14 @@ impl<'a> Unstructured<'a> { /// assert_eq!(remaining, [1, 2, 3]); /// ``` pub fn take_rest(mut self) -> &'a [u8] { - mem::replace(&mut self.data, &[]) + mem::take(&mut self.data) } /// Provide an iterator over elements for constructing a collection /// /// This is useful for implementing [`Arbitrary::arbitrary`] on collections /// since the implementation is simply `u.arbitrary_iter()?.collect()` - pub fn arbitrary_iter<'b, ElementType: Arbitrary>( + pub fn arbitrary_iter<'b, ElementType: Arbitrary<'a>>( &'b mut self, ) -> Result> { Ok(ArbitraryIter { @@ -494,7 +617,7 @@ impl<'a> Unstructured<'a> { /// /// This is useful for implementing [`Arbitrary::arbitrary_take_rest`] on collections /// since the implementation is simply `u.arbitrary_take_rest_iter()?.collect()` - pub fn arbitrary_take_rest_iter( + pub fn arbitrary_take_rest_iter>( self, ) -> Result> { let (lower, upper) = ElementType::size_hint(0); @@ -508,6 +631,88 @@ impl<'a> Unstructured<'a> { _marker: PhantomData, }) } + + /// Call the given function an arbitrary number of times. + /// + /// The function is given this `Unstructured` so that it can continue to + /// generate arbitrary data and structures. + /// + /// You may optionaly specify minimum and maximum bounds on the number of + /// times the function is called. + /// + /// You may break out of the loop early by returning + /// `Ok(std::ops::ControlFlow::Break)`. To continue the loop, return + /// `Ok(std::ops::ControlFlow::Continue)`. + /// + /// # Panics + /// + /// Panics if `min > max`. + /// + /// # Example + /// + /// Call a closure that generates an arbitrary type inside a context an + /// arbitrary number of times: + /// + /// ``` + /// use arbitrary::{Result, Unstructured}; + /// use std::ops::ControlFlow; + /// + /// enum Type { + /// /// A boolean type. + /// Bool, + /// + /// /// An integer type. + /// Int, + /// + /// /// A list of the `i`th type in this type's context. + /// List(usize), + /// } + /// + /// fn arbitrary_types_context(u: &mut Unstructured) -> Result> { + /// let mut context = vec![]; + /// + /// u.arbitrary_loop(Some(10), Some(20), |u| { + /// let num_choices = if context.is_empty() { + /// 2 + /// } else { + /// 3 + /// }; + /// let ty = match u.int_in_range::(1..=num_choices)? { + /// 1 => Type::Bool, + /// 2 => Type::Int, + /// 3 => Type::List(u.int_in_range(0..=context.len() - 1)?), + /// _ => unreachable!(), + /// }; + /// context.push(ty); + /// Ok(ControlFlow::Continue(())) + /// })?; + /// + /// // The number of loop iterations are constrained by the min/max + /// // bounds that we provided. + /// assert!(context.len() >= 10); + /// assert!(context.len() <= 20); + /// + /// Ok(context) + /// } + /// ``` + pub fn arbitrary_loop( + &mut self, + min: Option, + max: Option, + mut f: impl FnMut(&mut Self) -> Result>, + ) -> Result<()> { + let min = min.unwrap_or(0); + let max = max.unwrap_or(u32::MAX); + + for _ in 0..self.int_in_range(min..=max)? { + match f(self)? { + ControlFlow::Continue(_) => continue, + ControlFlow::Break(_) => break, + } + } + + Ok(()) + } } /// Utility iterator produced by [`Unstructured::arbitrary_iter`] @@ -516,7 +721,7 @@ pub struct ArbitraryIter<'a, 'b, ElementType> { _marker: PhantomData, } -impl<'a, 'b, ElementType: Arbitrary> Iterator for ArbitraryIter<'a, 'b, ElementType> { +impl<'a, 'b, ElementType: Arbitrary<'a>> Iterator for ArbitraryIter<'a, 'b, ElementType> { type Item = Result; fn next(&mut self) -> Option> { let keep_going = self.u.arbitrary().unwrap_or(false); @@ -535,7 +740,7 @@ pub struct ArbitraryTakeRestIter<'a, ElementType> { _marker: PhantomData, } -impl<'a, ElementType: Arbitrary> Iterator for ArbitraryTakeRestIter<'a, ElementType> { +impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, ElementType> { type Item = Result; fn next(&mut self) -> Option> { if let Some(mut u) = self.u.take() { @@ -573,6 +778,7 @@ impl<'a, ElementType: Arbitrary> Iterator for ArbitraryTakeRestIter<'a, ElementT /// Don't implement this trait yourself. pub trait Int: Copy + + std::fmt::Debug + PartialOrd + Ord + ops::Sub @@ -582,7 +788,7 @@ pub trait Int: + ops::BitOr { #[doc(hidden)] - type Widest: Int; + type Unsigned: Int; #[doc(hidden)] const ZERO: Self; @@ -591,10 +797,7 @@ pub trait Int: const ONE: Self; #[doc(hidden)] - fn as_widest(self) -> Self::Widest; - - #[doc(hidden)] - fn from_widest(w: Self::Widest) -> Self; + const MAX: Self; #[doc(hidden)] fn from_u8(b: u8) -> Self; @@ -607,26 +810,28 @@ pub trait Int: #[doc(hidden)] fn wrapping_add(self, rhs: Self) -> Self; + + #[doc(hidden)] + fn wrapping_sub(self, rhs: Self) -> Self; + + #[doc(hidden)] + fn to_unsigned(self) -> Self::Unsigned; + + #[doc(hidden)] + fn from_unsigned(unsigned: Self::Unsigned) -> Self; } macro_rules! impl_int { - ( $( $ty:ty : $widest:ty ; )* ) => { + ( $( $ty:ty : $unsigned_ty: ty ; )* ) => { $( impl Int for $ty { - type Widest = $widest; + type Unsigned = $unsigned_ty; const ZERO: Self = 0; const ONE: Self = 1; - fn as_widest(self) -> Self::Widest { - self as $widest - } - - fn from_widest(w: Self::Widest) -> Self { - let x = <$ty>::max_value().as_widest(); - (w % x) as Self - } + const MAX: Self = Self::MAX; fn from_u8(b: u8) -> Self { b as Self @@ -643,24 +848,36 @@ macro_rules! impl_int { fn wrapping_add(self, rhs: Self) -> Self { <$ty>::wrapping_add(self, rhs) } + + fn wrapping_sub(self, rhs: Self) -> Self { + <$ty>::wrapping_sub(self, rhs) + } + + fn to_unsigned(self) -> Self::Unsigned { + self as $unsigned_ty + } + + fn from_unsigned(unsigned: $unsigned_ty) -> Self { + unsigned as Self + } } )* } } impl_int! { - u8: u128; - u16: u128; - u32: u128; - u64: u128; + u8: u8; + u16: u16; + u32: u32; + u64: u64; u128: u128; - usize: u128; - i8: i128; - i16: i128; - i32: i128; - i64: i128; - i128: i128; - isize: i128; + usize: usize; + i8: u8; + i16: u16; + i32: u32; + i64: u64; + i128: u128; + isize: usize; } #[cfg(test)] @@ -691,4 +908,124 @@ mod tests { let choice = *u.choose(&[42]).unwrap(); assert_eq!(choice, 42) } + + #[test] + fn int_in_range_uses_minimal_amount_of_bytes() { + let mut u = Unstructured::new(&[1, 2]); + assert_eq!(1, u.int_in_range::(0..=u8::MAX).unwrap()); + assert_eq!(u.len(), 1); + + let mut u = Unstructured::new(&[1, 2]); + assert_eq!(1, u.int_in_range::(0..=u8::MAX as u32).unwrap()); + assert_eq!(u.len(), 1); + + let mut u = Unstructured::new(&[1]); + assert_eq!(1, u.int_in_range::(0..=u8::MAX as u32 + 1).unwrap()); + assert!(u.is_empty()); + } + + #[test] + fn int_in_range_in_bounds() { + for input in u8::MIN..=u8::MAX { + let input = [input]; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(1..=u8::MAX).unwrap(); + assert_ne!(x, 0); + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(0..=u8::MAX - 1).unwrap(); + assert_ne!(x, u8::MAX); + } + } + + #[test] + fn int_in_range_covers_unsigned_range() { + // Test that we generate all values within the range given to + // `int_in_range`. + + let mut full = [false; u8::MAX as usize + 1]; + let mut no_zero = [false; u8::MAX as usize]; + let mut no_max = [false; u8::MAX as usize]; + let mut narrow = [false; 10]; + + for input in u8::MIN..=u8::MAX { + let input = [input]; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(0..=u8::MAX).unwrap(); + full[x as usize] = true; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(1..=u8::MAX).unwrap(); + no_zero[x as usize - 1] = true; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(0..=u8::MAX - 1).unwrap(); + no_max[x as usize] = true; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(100..=109).unwrap(); + narrow[x as usize - 100] = true; + } + + for (i, covered) in full.iter().enumerate() { + assert!(covered, "full[{}] should have been generated", i); + } + for (i, covered) in no_zero.iter().enumerate() { + assert!(covered, "no_zero[{}] should have been generated", i); + } + for (i, covered) in no_max.iter().enumerate() { + assert!(covered, "no_max[{}] should have been generated", i); + } + for (i, covered) in narrow.iter().enumerate() { + assert!(covered, "narrow[{}] should have been generated", i); + } + } + + #[test] + fn int_in_range_covers_signed_range() { + // Test that we generate all values within the range given to + // `int_in_range`. + + let mut full = [false; u8::MAX as usize + 1]; + let mut no_min = [false; u8::MAX as usize]; + let mut no_max = [false; u8::MAX as usize]; + let mut narrow = [false; 21]; + + let abs_i8_min: isize = 128; + + for input in 0..=u8::MAX { + let input = [input]; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(i8::MIN..=i8::MAX).unwrap(); + full[(x as isize + abs_i8_min) as usize] = true; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(i8::MIN + 1..=i8::MAX).unwrap(); + no_min[(x as isize + abs_i8_min - 1) as usize] = true; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(i8::MIN..=i8::MAX - 1).unwrap(); + no_max[(x as isize + abs_i8_min) as usize] = true; + + let mut u = Unstructured::new(&input); + let x = u.int_in_range(-10..=10).unwrap(); + narrow[(x as isize + 10) as usize] = true; + } + + for (i, covered) in full.iter().enumerate() { + assert!(covered, "full[{}] should have been generated", i); + } + for (i, covered) in no_min.iter().enumerate() { + assert!(covered, "no_min[{}] should have been generated", i); + } + for (i, covered) in no_max.iter().enumerate() { + assert!(covered, "no_max[{}] should have been generated", i); + } + for (i, covered) in narrow.iter().enumerate() { + assert!(covered, "narrow[{}] should have been generated", i); + } + } } diff --git a/tests/derive.rs b/tests/derive.rs index 7f1e85e..f29d227 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,8 +1,11 @@ #![cfg(feature = "derive")] +// Various structs/fields that we are deriving `Arbitrary` for aren't actually +// used except to exercise the derive. +#![allow(dead_code)] use arbitrary::*; -fn arbitrary_from(input: &[u8]) -> T { +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") } @@ -21,15 +24,6 @@ fn struct_with_named_fields() { assert_eq!(rgb.g, 5); assert_eq!(rgb.b, 6); - assert_eq!( - rgb.shrink().collect::>(), - vec![ - Rgb { r: 0, g: 0, b: 0 }, - Rgb { r: 2, g: 2, b: 3 }, - Rgb { r: 1, g: 1, b: 1 } - ] - ); - assert_eq!((3, Some(3)), ::size_hint(0)); } @@ -46,11 +40,6 @@ fn tuple_struct() { assert_eq!(s.0, 42); assert_eq!(s.1, true); - for ((a, b), s) in 42.shrink().zip(true.shrink()).zip(s.shrink()) { - assert_eq!(a, s.0); - assert_eq!(b, s.1); - } - assert_eq!((2, Some(2)), ::size_hint(0)); } @@ -106,36 +95,16 @@ fn derive_enum() { match e { MyEnum::Unit => { saw_unit = true; - assert_eq!(e.shrink().count(), 0); } MyEnum::Tuple(a, b) => { saw_tuple = true; assert_eq!(a, arbitrary_from(&raw[4..5])); assert_eq!(b, arbitrary_from(&raw[5..])); - - for ((a, b), e) in a.shrink().zip(b.shrink()).zip(e.shrink()) { - match e { - MyEnum::Tuple(c, d) => { - assert_eq!(a, c); - assert_eq!(b, d); - } - _ => panic!("should never shrink to a different variant"), - } - } } MyEnum::Struct { a, b } => { saw_struct = true; assert_eq!(a, arbitrary_from(&raw[4..8])); assert_eq!(b, arbitrary_from(&raw[8..])); - for ((a, b), e) in a.shrink().zip(b.shrink()).zip(e.shrink()) { - match e { - MyEnum::Struct { a: c, b: d } => { - assert_eq!(a, c); - assert_eq!(b, d); - } - _ => panic!("should never shrink to a different variant"), - } - } } } } @@ -184,3 +153,126 @@ fn generics() { assert_eq!(lower, 4); assert_eq!(upper, Some(4)); } + +#[derive(Arbitrary, Debug)] +struct OneLifetime<'a> { + alpha: &'a str, +} + +#[test] +fn one_lifetime() { + // Last byte is used for length + let raw: Vec = vec![97, 98, 99, 100, 3]; + let lifetime: OneLifetime = arbitrary_from(&raw); + assert_eq!("abc", lifetime.alpha); + + let (lower, upper) = ::size_hint(0); + assert_eq!(lower, 0); + assert_eq!(upper, None); +} + +#[derive(Arbitrary, Debug)] +struct TwoLifetimes<'a, 'b> { + alpha: &'a str, + beta: &'b str, +} + +#[test] +fn two_lifetimes() { + // Last byte is used for length + let raw: Vec = vec![97, 98, 99, 100, 101, 102, 103, 3]; + let lifetime: TwoLifetimes = arbitrary_from(&raw); + assert_eq!("abc", lifetime.alpha); + assert_eq!("def", lifetime.beta); + + let (lower, upper) = ::size_hint(0); + assert_eq!(lower, 0); + assert_eq!(upper, None); +} + +#[test] +fn recursive_and_empty_input() { + // None of the following derives should result in a stack overflow. See + // https://github.com/rust-fuzz/arbitrary/issues/107 for details. + + #[derive(Debug, Arbitrary)] + enum Nat { + Succ(Box), + Zero, + } + + let _ = Nat::arbitrary(&mut Unstructured::new(&[])); + + #[derive(Debug, Arbitrary)] + enum Nat2 { + Zero, + Succ(Box), + } + + let _ = Nat2::arbitrary(&mut Unstructured::new(&[])); + + #[derive(Debug, Arbitrary)] + struct Nat3 { + f: Option>, + } + + let _ = Nat3::arbitrary(&mut Unstructured::new(&[])); + + #[derive(Debug, Arbitrary)] + struct Nat4(Option>); + + let _ = Nat4::arbitrary(&mut Unstructured::new(&[])); + + #[derive(Debug, Arbitrary)] + enum Nat5 { + Zero, + Succ { f: Box }, + } + + let _ = Nat5::arbitrary(&mut Unstructured::new(&[])); +} + +#[test] +fn test_field_attributes() { + // A type that DOES NOT implement Arbitrary + #[derive(Debug)] + struct Weight(u8); + + #[derive(Debug, Arbitrary)] + struct Parcel { + #[arbitrary(with = arbitrary_weight)] + weight: Weight, + + #[arbitrary(default)] + width: u8, + + #[arbitrary(value = 2 + 2)] + length: u8, + + height: u8, + + #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=100))] + price: u8, + } + + fn arbitrary_weight(u: &mut Unstructured) -> arbitrary::Result { + u.int_in_range(45..=56).map(Weight) + } + + let parcel: Parcel = arbitrary_from(&[6, 199, 17]); + + // 45 + 6 = 51 + assert_eq!(parcel.weight.0, 51); + + // u8::default() + assert_eq!(parcel.width, 0); + + // 2 + 2 = 4 + assert_eq!(parcel.length, 4); + + // 199 is the 2nd byte used by arbitrary + assert_eq!(parcel.height, 199); + + // 17 is the 3rd byte used by arbitrary + assert_eq!(parcel.price, 17); +} diff --git a/tests/path.rs b/tests/path.rs index 15dbbe3..c42ec0a 100644 --- a/tests/path.rs +++ b/tests/path.rs @@ -1,4 +1,7 @@ #![cfg(feature = "derive")] +// Various structs/fields that we are deriving `Arbitrary` for aren't actually +// used except to show off the derive. +#![allow(dead_code)] // Regression test for ensuring the derives work without Arbitrary being imported