aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2022-12-07 12:42:00 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-12-07 12:42:00 +0000
commit210942dc035609c9d12d9878ee5fbbb009a0fe8b (patch)
treeb6b678de9a769993df7242ef941190ae5c64d8b5
parent39e5e7c8e4fafdd449d9b4aeae34de94c1024bae (diff)
parent436d40a2e5410dd4db86259630d1cc9d2b4dfc4a (diff)
downloadderive_arbitrary-210942dc035609c9d12d9878ee5fbbb009a0fe8b.tar.gz
Upgrade derive_arbitrary to 1.2.0 am: 436d40a2e5main-16k-with-phones
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/derive_arbitrary/+/2327954 Change-Id: I7a02f3fc90c91ba227f13e0dabe1afbea8dac571 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp4
-rw-r--r--Cargo.toml6
-rw-r--r--Cargo.toml.orig5
-rw-r--r--METADATA12
-rw-r--r--src/field_attributes.rs117
-rw-r--r--src/lib.rs238
7 files changed, 296 insertions, 88 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 30c0308..0724802 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "8e7b857f4f78b06920a36212ed2f392bc523c1f6"
+ "sha1": "48c4bfd52b167da94a5db906fe7dd2da7b92215e"
},
"path_in_vcs": "derive"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index e5183a8..04e8dca 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,9 +43,9 @@ rust_proc_macro {
name: "libderive_arbitrary",
crate_name: "derive_arbitrary",
cargo_env_compat: true,
- cargo_pkg_version: "1.1.3",
+ cargo_pkg_version: "1.2.0",
srcs: ["src/lib.rs"],
- edition: "2018",
+ edition: "2021",
rustlibs: [
"libproc_macro2",
"libquote",
diff --git a/Cargo.toml b/Cargo.toml
index 68714a1..db2f6be 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,9 +10,10 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
+edition = "2021"
+rust-version = "1.63.0"
name = "derive_arbitrary"
-version = "1.1.3"
+version = "1.2.0"
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <fitzgen@gmail.com>",
@@ -32,6 +33,7 @@ keywords = [
categories = ["development-tools::testing"]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary"
+resolver = "1"
[lib]
proc_macro = true
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 6896426..f83eff0 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "derive_arbitrary"
-version = "1.1.3" # Make sure it matches the version of the arbitrary crate itself.
+version = "1.2.0" # Make sure it matches the version of the arbitrary crate itself.
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <fitzgen@gmail.com>",
@@ -9,13 +9,14 @@ authors = [
"Corey Farwell <coreyf@rwell.org>",
]
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/rust-fuzz/arbitrary"
documentation = "https://docs.rs/arbitrary/"
+rust-version = "1.63.0"
[dependencies]
proc-macro2 = "1.0"
diff --git a/METADATA b/METADATA
index 5ee0f1e..b3c0616 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/derive_arbitrary
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "derive_arbitrary"
description: "Derives arbitrary traits"
third_party {
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.1.3.crate"
+ value: "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.2.0.crate"
}
- version: "1.1.3"
+ version: "1.2.0"
license_type: NOTICE
last_upgrade_date {
year: 2022
- month: 6
- day: 27
+ month: 12
+ day: 5
}
}
diff --git a/src/field_attributes.rs b/src/field_attributes.rs
new file mode 100644
index 0000000..ccaba74
--- /dev/null
+++ b/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<FieldConstructor> {
+ 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<Option<&Attribute>> {
+ 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<FieldConstructor> {
+ 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<FieldConstructor> {
+ 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<Item = TokenTree>,
+ default_span: Span,
+) -> Result<TokenStream> {
+ 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/src/lib.rs b/src/lib.rs
index 7b16a8d..4cd80b6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,11 +4,20 @@ use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::*;
+mod field_attributes;
+use field_attributes::{determine_field_constructor, FieldConstructor};
+
static ARBITRARY_LIFETIME_NAME: &str = "'arbitrary";
-#[proc_macro_derive(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()
+}
+
+fn expand_derive_arbitrary(input: syn::DeriveInput) -> Result<TokenStream> {
let (lifetime_without_bounds, lifetime_with_bounds) =
build_arbitrary_lifetime(input.generics.clone());
@@ -18,8 +27,8 @@ pub fn derive_arbitrary(tokens: proc_macro::TokenStream) -> proc_macro::TokenStr
);
let arbitrary_method =
- gen_arbitrary_method(&input, lifetime_without_bounds.clone(), &recursive_count);
- let size_hint_method = gen_size_hint_method(&input);
+ 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, lifetime_without_bounds.clone());
@@ -34,7 +43,7 @@ pub fn derive_arbitrary(tokens: proc_macro::TokenStream) -> proc_macro::TokenStr
// Build TypeGenerics and WhereClause without a lifetime
let (_, ty_generics, where_clause) = generics.split_for_impl();
- (quote! {
+ Ok(quote! {
const _: () = {
thread_local! {
#[allow(non_upper_case_globals)]
@@ -47,7 +56,6 @@ pub fn derive_arbitrary(tokens: proc_macro::TokenStream) -> proc_macro::TokenStr
}
};
})
- .into()
}
// Returns: (lifetime without bounds, lifetime with bounds)
@@ -112,18 +120,21 @@ fn gen_arbitrary_method(
input: &DeriveInput,
lifetime: LifetimeDef,
recursive_count: &syn::Ident,
-) -> TokenStream {
- let ident = &input.ident;
-
- let arbitrary_structlike = |fields| {
- let arbitrary = construct(fields, |_, _| quote!(arbitrary::Arbitrary::arbitrary(u)?));
+) -> Result<TokenStream> {
+ fn arbitrary_structlike(
+ fields: &Fields,
+ ident: &syn::Ident,
+ lifetime: LifetimeDef,
+ 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 arbitrary_take_rest = construct_take_rest(fields);
+ let arbitrary_take_rest = construct_take_rest(fields)?;
let take_rest_body =
with_recursive_count_guard(recursive_count, quote! { Ok(#ident #arbitrary_take_rest) });
- quote! {
+ Ok(quote! {
fn arbitrary(u: &mut arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
#body
}
@@ -131,27 +142,43 @@ fn gen_arbitrary_method(
fn arbitrary_take_rest(mut u: arbitrary::Unstructured<#lifetime>) -> arbitrary::Result<Self> {
#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<TokenStream> = data
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(i, variant)| {
+ let idx = i as u64;
+ let variant_name = &variant.ident;
+ construct(&variant.fields, |_, field| gen_constructor_for_field(field))
+ .map(|ctor| quote! { #idx => #ident::#variant_name #ctor })
+ })
+ .collect::<Result<_>>()?;
+
+ let variants_take_rest: Vec<TokenStream> = data
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(i, variant)| {
+ let idx = i as u64;
+ let variant_name = &variant.ident;
+ construct_take_rest(&variant.fields)
+ .map(|ctor| quote! { #idx => #ident::#variant_name #ctor })
+ })
+ .collect::<Result<_>>()?;
+
let count = data.variants.len() as u64;
let arbitrary = with_recursive_count_guard(
@@ -190,74 +217,131 @@ fn gen_arbitrary_method(
}
}
}
- }
+ };
+ 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<TokenStream>,
+) -> Result<TokenStream> {
+ 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<TokenStream> = names
+ .named
+ .iter()
+ .enumerate()
+ .map(|(i, f)| {
+ let name = f.ident.as_ref().unwrap();
+ ctor(i, f).map(|ctor| quote! { #name: #ctor })
+ })
+ .collect::<Result<_>>()?;
quote! { { #(#names,)* } }
}
Fields::Unnamed(names) => {
- let names = names.unnamed.iter().enumerate().map(|(i, f)| {
- let ctor = ctor(i, f);
- quote! { #ctor }
- });
+ let names: Vec<TokenStream> = names
+ .unnamed
+ .iter()
+ .enumerate()
+ .map(|(i, f)| ctor(i, f).map(|ctor| quote! { #ctor }))
+ .collect::<Result<_>>()?;
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<TokenStream> {
+ 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<TokenStream> {
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::<T>() is
+ // just an educated guess, although it's gonna be inaccurate for dynamically
+ // allocated types (Vec, HashMap, etc.).
+ FieldConstructor::With(_) => {
+ quote! { (::core::mem::size_of::<#ty>(), None) }
+ }
+ }
+ })
+ })
+ .collect::<Result<Vec<TokenStream>>>()
+ .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<usize>) {
- arbitrary::size_hint::recursion_guard(depth, |depth| #hint)
+ size_hint_fields(fields).map(|hint| {
+ quote! {
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ 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<usize>) {
- arbitrary::size_hint::and(
- <u32 as arbitrary::Arbitrary>::size_hint(depth),
- arbitrary::size_hint::recursion_guard(depth, |depth| {
- arbitrary::size_hint::or_all(&[ #( #variants ),* ])
- }),
- )
+ Data::Enum(data) => data
+ .variants
+ .iter()
+ .map(|v| size_hint_fields(&v.fields))
+ .collect::<Result<Vec<TokenStream>>>()
+ .map(|variants| {
+ quote! {
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ arbitrary::size_hint::and(
+ <u32 as arbitrary::Arbitrary>::size_hint(depth),
+ arbitrary::size_hint::recursion_guard(depth, |depth| {
+ arbitrary::size_hint::or_all(&[ #( #variants ),* ])
+ }),
+ )
+ }
}
- }
- }
+ }),
}
}
+
+fn gen_constructor_for_field(field: &Field) -> Result<TokenStream> {
+ 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),
+ };
+ Ok(ctor)
+}