Skip to content

Add no_std Support #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
13 changes: 11 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: rustup toolchain add 1.63
- run: rustup toolchain add 1.81
- name: Build
run: cargo +1.63 check --lib --all-features
run: cargo +1.81 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.81
- run: rustup target add x86_64-unknown-none --toolchain 1.81
- run: cargo +1.81 check --no-default-features --features alloc,derive --target x86_64-unknown-none
clippy:
name: Clippy
runs-on: ubuntu-latest
Expand Down
16 changes: 14 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ description = "The trait for generating structured data from unstructured data"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary/"
documentation = "https://docs.rs/arbitrary/"
rust-version = "1.63.0" # Keep in sync with version documented in the README.md
rust-version = "1.81.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 = []
# 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 +47,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"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl<'a> Arbitrary<'a> for Rgb {

<!-- NB: Keep this number in sync with the `rust-version` in `Cargo.toml`. -->

This crate is guaranteed to compile on stable Rust **1.63.0** and up. It might
This crate is guaranteed to compile on stable Rust **1.81.0** and up. It might
compile with older versions but that may change in any new patch release.

We reserve the right to increment the MSRV on minor releases, however we will
Expand Down
7 changes: 6 additions & 1 deletion derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ description = "Derives arbitrary traits"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary"
documentation = "https://docs.rs/arbitrary/"
rust-version = "1.63.0"
rust-version = "1.81.0"

[dependencies]
proc-macro2 = "1.0"
Expand All @@ -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
12 changes: 8 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,24 @@ impl fmt::Display for Error {
}
}

impl error::Error for 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
4 changes: 3 additions & 1 deletion src/foreign/alloc/ffi/c_str.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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/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
Loading