Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ jobs:
- run: rustup toolchain add 1.63
- name: Build
run: cargo +1.63 check --lib --all-features
check_no_std:
name: Check no_std
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: rustup update
- run: rustup toolchain add 1.82
- run: rustup target add x86_64-unknown-none --toolchain 1.82
- run: cargo +1.82 check --no-default-features --features alloc,derive,core_error,core_net,alloc_ffi_cstring,derive --target x86_64-unknown-none
clippy:
name: Clippy
runs-on: ubuntu-latest
Expand Down
20 changes: 19 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,24 @@ documentation = "https://docs.rs/arbitrary/"
rust-version = "1.63.0" # Keep in sync with version documented in the README.md

[dependencies]
derive_arbitrary = { version = "~1.4.0", path = "./derive", optional = true }
derive_arbitrary = { version = "~1.4.0", path = "./derive", default-features = false, optional = true }

[features]
default = ["std", "recursive_count"]
# Turn this feature on to enable support for `#[derive(Arbitrary)]`.
derive = ["derive_arbitrary"]
# Enables support for the `std` crate.
std = ["alloc"]
# Enables support for the `alloc` crate.
alloc = []
# Enables using core::error::Error. Increases MSRV to at least 1.81.0
core_error = []
# Enables using core::net when `std` is disabled. Increases MSRV to at least 1.77.0
core_net = []
# Enables using `alloc::ffi::CString` when `std` is disabled. Increases MSRV to at least 1.64.0
alloc_ffi_cstring = ["alloc"]
# Checks for unbounded recursion at runtime. This does nothing without the "derive" feature.
recursive_count = ["derive_arbitrary?/recursive_count"]

[[example]]
name = "derive_enum"
Expand All @@ -40,3 +53,8 @@ members = ["./fuzz"]

[dev-dependencies]
exhaustigen = "0.1.0"

[lints.clippy]
alloc_instead_of_core = "warn"
std_instead_of_alloc = "warn"
std_instead_of_core = "warn"
5 changes: 5 additions & 0 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ syn = { version = "2", features = ['derive', 'parsing', 'extra-traits'] }

[lib]
proc-macro = true

[features]
default = ["recursive_count"]
# Checks for unbounded recursion at runtime. Requires `std`.
recursive_count = []
86 changes: 68 additions & 18 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,18 @@ fn expand_derive_arbitrary(input: syn::DeriveInput) -> Result<TokenStream> {
let (lifetime_without_bounds, lifetime_with_bounds) =
build_arbitrary_lifetime(input.generics.clone());

#[cfg(feature = "recursive_count")]
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 arbitrary_method = gen_arbitrary_method(
&input,
lifetime_without_bounds.clone(),
#[cfg(feature = "recursive_count")]
&recursive_count,
)?;
let size_hint_method = gen_size_hint_method(&input)?;
let name = input.ident;

Expand All @@ -56,12 +61,22 @@ fn expand_derive_arbitrary(input: syn::DeriveInput) -> Result<TokenStream> {
// Build TypeGenerics and WhereClause without a lifetime
let (_, ty_generics, where_clause) = generics.split_for_impl();

#[cfg(feature = "recursive_count")]
let recursive_count_preamble = quote! {
extern crate std;

::std::thread_local! {
#[allow(non_upper_case_globals)]
static #recursive_count: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);
}
};

#[cfg(not(feature = "recursive_count"))]
let recursive_count_preamble = TokenStream::new();

Ok(quote! {
const _: () = {
::std::thread_local! {
#[allow(non_upper_case_globals)]
static #recursive_count: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);
}
#recursive_count_preamble

#[automatically_derived]
impl #impl_generics arbitrary::Arbitrary<#lifetime_without_bounds> for #name #ty_generics #where_clause {
Expand Down Expand Up @@ -148,9 +163,10 @@ fn add_trait_bounds(mut generics: Generics, lifetime: LifetimeParam) -> Generics
}

fn with_recursive_count_guard(
recursive_count: &syn::Ident,
#[cfg(feature = "recursive_count")] recursive_count: &syn::Ident,
expr: impl quote::ToTokens,
) -> impl quote::ToTokens {
#[cfg(feature = "recursive_count")]
quote! {
let guard_against_recursion = u.is_empty();
if guard_against_recursion {
Expand All @@ -173,25 +189,35 @@ fn with_recursive_count_guard(

result
}

#[cfg(not(feature = "recursive_count"))]
quote! { (|| { #expr })() }
}

fn gen_arbitrary_method(
input: &DeriveInput,
lifetime: LifetimeParam,
recursive_count: &syn::Ident,
#[cfg(feature = "recursive_count")] recursive_count: &syn::Ident,
) -> Result<TokenStream> {
fn arbitrary_structlike(
fields: &Fields,
ident: &syn::Ident,
lifetime: LifetimeParam,
recursive_count: &syn::Ident,
#[cfg(feature = "recursive_count")] recursive_count: &syn::Ident,
) -> Result<TokenStream> {
let arbitrary = construct(fields, |_idx, field| gen_constructor_for_field(field))?;
let body = with_recursive_count_guard(recursive_count, quote! { Ok(#ident #arbitrary) });
let body = with_recursive_count_guard(
#[cfg(feature = "recursive_count")]
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) });
let take_rest_body = with_recursive_count_guard(
#[cfg(feature = "recursive_count")]
recursive_count,
quote! { Ok(#ident #arbitrary_take_rest) },
);

Ok(quote! {
fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
Expand All @@ -214,12 +240,13 @@ fn gen_arbitrary_method(
}

fn arbitrary_enum_method(
recursive_count: &syn::Ident,
unstructured: TokenStream,
variants: &[TokenStream],
#[cfg(feature = "recursive_count")] recursive_count: &syn::Ident,
) -> impl quote::ToTokens {
let count = variants.len() as u64;
with_recursive_count_guard(
#[cfg(feature = "recursive_count")]
recursive_count,
quote! {
// Use a multiply + shift to generate a ranged random number
Expand All @@ -237,7 +264,7 @@ fn gen_arbitrary_method(
DataEnum { variants, .. }: &DataEnum,
enum_name: &Ident,
lifetime: LifetimeParam,
recursive_count: &syn::Ident,
#[cfg(feature = "recursive_count")] recursive_count: &syn::Ident,
) -> Result<TokenStream> {
let filtered_variants = variants.iter().filter(not_skipped);

Expand Down Expand Up @@ -275,8 +302,18 @@ fn gen_arbitrary_method(
(!variants.is_empty())
.then(|| {
// TODO: Improve dealing with `u` vs. `&mut u`.
let arbitrary = arbitrary_enum_method(recursive_count, quote! { u }, &variants);
let arbitrary_take_rest = arbitrary_enum_method(recursive_count, quote! { &mut u }, &variants_take_rest);
let arbitrary = arbitrary_enum_method(
quote! { u },
&variants,
#[cfg(feature = "recursive_count")]
recursive_count,
);
let arbitrary_take_rest = arbitrary_enum_method(
quote! { &mut u },
&variants_take_rest,
#[cfg(feature = "recursive_count")]
recursive_count,
);

quote! {
fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
Expand All @@ -296,14 +333,27 @@ fn gen_arbitrary_method(

let ident = &input.ident;
match &input.data {
Data::Struct(data) => arbitrary_structlike(&data.fields, ident, lifetime, recursive_count),
Data::Struct(data) => arbitrary_structlike(
&data.fields,
ident,
lifetime,
#[cfg(feature = "recursive_count")]
recursive_count,
),
Data::Union(data) => arbitrary_structlike(
&Fields::Named(data.fields.clone()),
ident,
lifetime,
#[cfg(feature = "recursive_count")]
recursive_count,
),
Data::Enum(data) => arbitrary_enum(
data,
ident,
lifetime,
#[cfg(feature = "recursive_count")]
recursive_count,
),
Data::Enum(data) => arbitrary_enum(data, ident, lifetime, recursive_count),
}
}

Expand Down
16 changes: 12 additions & 4 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{error, fmt};
use core::fmt;

/// An enumeration of buffer creation errors
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -32,20 +32,28 @@ impl fmt::Display for Error {
}
}

impl error::Error for Error {}
#[cfg(feature = "std")]
impl std::error::Error for Error {}

#[cfg(all(not(feature = "std"), feature = "core_error"))]
impl core::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<T, E = Error> = std::result::Result<T, E>;
pub type Result<T, E = Error> = core::result::Result<T, E>;

#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
use alloc::string::String;

// 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
// 0.1% as `core::result::Result`; instead, let `arbitrary::Result` have an
// overridable error type.
#[cfg(feature = "alloc")]
#[test]
fn can_use_custom_error_types_with_result() -> super::Result<(), String> {
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/borrow.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
std::borrow::{Cow, ToOwned},
alloc::borrow::{Cow, ToOwned},
};

impl<'a, A> Arbitrary<'a> for Cow<'a, A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/boxed.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
std::boxed::Box,
alloc::{boxed::Box, string::String},
};

impl<'a, A> Arbitrary<'a> for Box<A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/collections/binary_heap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::binary_heap::BinaryHeap,
alloc::collections::binary_heap::BinaryHeap,
};

impl<'a, A> Arbitrary<'a> for BinaryHeap<A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/collections/btree_map.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::btree_map::BTreeMap,
alloc::collections::btree_map::BTreeMap,
};

impl<'a, K, V> Arbitrary<'a> for BTreeMap<K, V>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/collections/btree_set.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::btree_set::BTreeSet,
alloc::collections::btree_set::BTreeSet,
};

impl<'a, A> Arbitrary<'a> for BTreeSet<A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/collections/linked_list.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::linked_list::LinkedList,
alloc::collections::linked_list::LinkedList,
};

impl<'a, A> Arbitrary<'a> for LinkedList<A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/collections/vec_deque.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::vec_deque::VecDeque,
alloc::collections::vec_deque::VecDeque,
};

impl<'a, A> Arbitrary<'a> for VecDeque<A>
Expand Down
8 changes: 7 additions & 1 deletion src/foreign/alloc/ffi/c_str.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#[cfg(feature = "std")]
use std::ffi::CString;

#[cfg(all(not(feature = "std"), feature = "alloc_ffi_cstring"))]
use alloc::ffi::CString;

use {
crate::{Arbitrary, Result, Unstructured},
std::ffi::CString,
alloc::vec::Vec,
};

impl<'a> Arbitrary<'a> for CString {
Expand Down
1 change: 1 addition & 0 deletions src/foreign/alloc/ffi/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#[cfg(any(feature = "std", feature = "alloc_ffi_cstring"))]
mod c_str;
1 change: 1 addition & 0 deletions src/foreign/alloc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ mod collections;
mod ffi;
mod rc;
mod string;
#[cfg(target_has_atomic = "ptr")]
mod sync;
mod vec;
2 changes: 1 addition & 1 deletion src/foreign/alloc/rc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
std::rc::Rc,
alloc::rc::Rc,
};

impl<'a, A> Arbitrary<'a> for Rc<A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/string.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::string::String,
alloc::string::String,
};

impl<'a> Arbitrary<'a> for String {
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
std::sync::Arc,
alloc::sync::Arc,
};

impl<'a, A> Arbitrary<'a> for Arc<A>
Expand Down
2 changes: 1 addition & 1 deletion src/foreign/alloc/vec.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::vec::Vec,
alloc::vec::Vec,
};

impl<'a, A> Arbitrary<'a> for Vec<A>
Expand Down
2 changes: 2 additions & 0 deletions src/foreign/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ mod char;
mod cmp;
mod iter;
mod marker;
#[cfg(any(feature = "std", feature = "core_net"))]
mod net;
mod num;
mod ops;
mod option;
Expand Down
Loading