diff --git a/proptest-derive/Cargo.toml b/proptest-derive/Cargo.toml new file mode 100644 index 00000000..7bea2580 --- /dev/null +++ b/proptest-derive/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "proptest-derive" +version = "0.1.0" +authors = ["Mazdak Farrokhzad "] +license = "MIT/Apache-2.0" +readme = "README.md" + +repository = "https://github.com/AltSysrq/proptest" +documentation = "https://docs.rs/proptest-derive" + +keywords = ["derive", "arbitrary", "proptest", "testing", "quickcheck"] +categories = ["development-tools::testing"] + +description = """ +Custom-derive for the Arbitrary trait of proptest. +""" + +[badges] + +maintenance = { status = "experimental" } + +[lib] +proc-macro = true + +[dev-dependencies] +proptest = { path = "../" } +compiletest_rs = { version = "0.3.3", features = ["tmp"] } + +[dependencies] +proc-macro2 = "0.4" + +syn = { version = "0.14.5", features = ["visit", "extra-traits", "full"] } +quote = "0.6" diff --git a/proptest-derive/README.md b/proptest-derive/README.md new file mode 100644 index 00000000..dae22195 --- /dev/null +++ b/proptest-derive/README.md @@ -0,0 +1,5 @@ +# proptest-derive + +Custom-derive for the Arbitrary trait of proptest. + +This is currently experimental. \ No newline at end of file diff --git a/proptest-derive/src/ast.rs b/proptest-derive/src/ast.rs new file mode 100644 index 00000000..4901d7c9 --- /dev/null +++ b/proptest-derive/src/ast.rs @@ -0,0 +1,712 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! High level IR and abstract syntax tree (AST) of impls. +//! +//! We compile to this AST and then linearise that to Rust code. + +use std::ops::{Add, AddAssign}; + +use syn; +use proc_macro2::{TokenStream, Span}; +use quote::{ToTokens, TokenStreamExt}; + +use util::self_ty; +use use_tracking::UseTracker; +use error::{Ctx, DeriveResult}; + +//============================================================================== +// Config +//============================================================================== + +/// The `MAX - 1` number of strategies that `TupleUnion` supports. +/// Increase this if the behaviour is changed in `proptest`. +/// Keeping this lower than what `proptest` supports will also work +/// but for optimality this should follow what `proptest` supports. +const UNION_CHUNK_SIZE: usize = 9; + +/// The name of the top parameter variable name given in `arbitrary_with`. +/// Changing this is not a breaking change because a user is expected not +/// to rely on this (and the user shouldn't be able to..). +const TOP_PARAM_NAME: &str = "_top"; + +/// The name of the variable name used for user facing parameter types +/// specified in a `#[proptest(params = ""]` attribute. +/// +/// Changing the value of this constant constitutes a breaking change! +const API_PARAM_NAME: &str = "params"; + +//============================================================================== +// AST Root +//============================================================================== + +/// Top level AST and everything required to implement `Arbitrary` for any +/// given type. Linearizing this AST gives you the impl wrt. Rust code. +pub struct Impl { + /// Name of the type. + typ: syn::Ident, + /// Tracker for uses of Arbitrary trait for a generic type. + tracker: UseTracker, + /// The three main parts, see description of `ImplParts` for details. + parts: ImplParts, +} + +/// The three main parts to deriving `Arbitrary` for a type. +/// That is: the associated items `Parameters` (`Params`), +/// `Strategy` (`Strategy`) as well as the construction of the +/// strategy itself (`Ctor`). +pub type ImplParts = (Params, Strategy, Ctor); + +impl Impl { + /// Constructs a new `Impl` from the parts as described on the type. + pub fn new(typ: syn::Ident, tracker: UseTracker, parts: ImplParts) -> Self { + Self { typ, tracker, parts } + } + + /// Linearises the impl into a sequence of tokens. + /// This produces the actual Rust code for the impl. + pub fn into_tokens(self, ctx: Ctx) -> DeriveResult { + let Impl { typ, mut tracker, parts: (params, strategy, ctor) } = self; + + /// A `Debug` bound on a type variable. + fn debug_bound() -> syn::TypeParamBound { + parse_quote!( ::std::fmt::Debug ) + } + + /// An `Arbitrary` bound on a type variable. + fn arbitrary_bound() -> syn::TypeParamBound { + parse_quote!( _proptest::arbitrary::Arbitrary ) + } + + // Add bounds and get generics for the impl. + tracker.add_bounds(ctx, &arbitrary_bound(), Some(debug_bound()))?; + let generics = tracker.consume(); + let (impl_generics, ty_generics, where_clause) + = generics.split_for_impl(); + + let _top = call_site_ident(TOP_PARAM_NAME); + + let _const = call_site_ident( + &format!("_IMPL_ARBITRARY_FOR_{}", typ)); + + // Linearise everything. We're done after this. + let q = quote! { + #[allow(non_upper_case_globals)] + const #_const: () = { + extern crate proptest as _proptest; + + impl #impl_generics _proptest::arbitrary::Arbitrary + for #typ #ty_generics #where_clause { + type Parameters = #params; + + type Strategy = #strategy; + + fn arbitrary_with(#_top: Self::Parameters) -> Self::Strategy { + #ctor + } + } + + }; + }; + + Ok(q) + } +} + +//============================================================================== +// Smart construcors, StratPair +//============================================================================== + +/// A pair of `Strategy` and `Ctor`. These always come in pairs. +pub type StratPair = (Strategy, Ctor); + +/// The type and constructor for `any::()`. +pub fn pair_any(ty: syn::Type, span: Span) -> StratPair { + let q = Ctor::Arbitrary(ty.clone(), None, span); + (Strategy::Arbitrary(ty, span), q) +} + +/// The type and constructor for `any_with::(parameters)`. +pub fn pair_any_with(ty: syn::Type, var: usize, span: Span) -> StratPair { + let q = Ctor::Arbitrary(ty.clone(), Some(var), span); + (Strategy::Arbitrary(ty, span), q) +} + +/// The type and constructor for a specific strategy value constructed by the +/// given expression. Currently, the type is erased and a `BoxedStrategy` +/// is given back instead. +/// +/// This is a temporary restriction. Once `impl Trait` is stabilized, +/// the boxing and dynamic dispatch can be replaced with a statically +/// dispatched anonymous type instead. +pub fn pair_existential(ty: syn::Type, strat: syn::Expr) -> StratPair { + (Strategy::Existential(ty), Ctor::Existential(strat)) +} + +/// The type and constructor for a strategy that always returns the value +/// provided in the expression `val`. +/// This is statically dispatched since no erasure is needed or used. +pub fn pair_value(ty: syn::Type, val: syn::Expr) -> StratPair { + (Strategy::Value(ty), Ctor::Value(val)) +} + +/// Same as `pair_existential` for the `Self` type. +pub fn pair_existential_self(strat: syn::Expr) -> StratPair { + pair_existential(self_ty(), strat) +} + +/// Same as `pair_value` for the `Self` type. +pub fn pair_value_self(val: syn::Expr) -> StratPair { + pair_value(self_ty(), val) +} + +/// Erased strategy for a fixed value. +pub fn pair_value_exist(ty: syn::Type, strat: syn::Expr) -> StratPair { + (Strategy::Existential(ty), Ctor::ValueExistential(strat)) +} + +/// Erased strategy for a fixed value. +pub fn pair_value_exist_self(strat: syn::Expr) -> StratPair { + pair_value_exist(self_ty(), strat) +} + +/// Same as `pair_value` but for a unit variant or unit struct. +pub fn pair_unit_self(path: &syn::Path) -> StratPair { + pair_value_self(parse_quote!( #path {} )) +} + +/// The type and constructor for .prop_map:ing a set of strategies +/// into the type we are implementing for. The closure for the +/// `.prop_map()` must also be given. +pub fn pair_map((strats, ctors): (Vec, Vec), closure: MapClosure) + -> StratPair +{ + (Strategy::Map(strats.into()), Ctor::Map(ctors.into(), closure)) +} + +/// The type and constructor for a union of strategies which produces a new +/// strategy that used the given strategies with probabilities based on the +/// assigned relative weights for each strategy. +pub fn pair_oneof((strats, ctors): (Vec, Vec<(u32, Ctor)>)) + -> StratPair +{ + (Strategy::Union(strats.into()), Ctor::Union(ctors.into())) +} + +/// Potentially apply a filter to a strategy type and its constructor. +pub fn pair_filter(filter: Vec, ty: syn::Type, pair: StratPair) + -> StratPair +{ + filter.into_iter().fold(pair, |(strat, ctor), filter| ( + Strategy::Filter(Box::new(strat), ty.clone()), + Ctor::Filter(Box::new(ctor), filter) + )) +} + +//============================================================================== +// Parameters +//============================================================================== + +/// Represents the associated item of `Parameters` of an `Arbitrary` impl. +pub struct Params(Vec); + +impl Params { + /// Construct an `empty` list of parameters. + /// This is equivalent to the unit type `()`. + pub fn empty() -> Self { + Params(Vec::new()) + } + + /// Computes and returns the number of parameter types. + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl From for syn::Type { + fn from(x: Params) -> Self { + let tys = x.0; + parse_quote!( (#(#tys),*) ) + } +} + +impl Add for Params { + type Output = Params; + + fn add(mut self, rhs: syn::Type) -> Self::Output { + self.0.push(rhs); + self + } +} + +impl AddAssign for Params { + fn add_assign(&mut self, rhs: syn::Type) { + self.0.push(rhs); + } +} + +impl ToTokens for Params { + fn to_tokens(&self, tokens: &mut TokenStream) { + Tuple2(self.0.as_slice()).to_tokens(tokens) + } +} + +/// Returns for a given type `ty` the associated item `Parameters` of the +/// type's `Arbitrary` implementation. +pub fn arbitrary_param(ty: &syn::Type) -> syn::Type { + parse_quote!(<#ty as _proptest::arbitrary::Arbitrary>::Parameters) +} + +//============================================================================== +// Strategy +//============================================================================== + +/// The type of a given `Strategy`. +pub enum Strategy { + /// Assuming the metavariable `$ty` for a given type, this models the + /// strategy type `<$ty as Arbitrary>::Strategy`. + Arbitrary(syn::Type, Span), + /// Assuming the metavariable `$ty` for a given type, this models the + /// strategy type `BoxedStrategy<$ty>`, i.e: an existentially typed strategy. + /// + /// The dynamic dispatch used here is an implementation detail that may be + /// changed. Such a change does not count as a breakage semver wise. + Existential(syn::Type), + /// Assuming the metavariable `$ty` for a given type, this models a + /// non-shrinking strategy that simply always returns a value of the + /// given type. + Value(syn::Type), + /// Assuming a sequence of strategies, this models a mapping from that + /// sequence to `Self`. + Map(Box<[Strategy]>), + /// Assuming a sequence of relative-weighted strategies, this models a + /// weighted choice of those strategies. The resultant strategy will in + /// other words randomly pick one strategy with probabilities based on the + /// specified weights. + Union(Box<[Strategy]>), + /// A filtered strategy with `.prop_filter`. + Filter(Box, syn::Type), +} + +macro_rules! quote_append { + ($tokens: expr, $($quasi: tt)*) => { + $tokens.append_all(quote!($($quasi)*)) + }; +} + +impl Strategy { + fn types(&self) -> Vec { + use self::Strategy::*; + match self { + Arbitrary(ty, _) => vec![ty.clone()], + Existential(ty) => vec![ty.clone()], + Value(ty) => vec![ty.clone()], + Map(strats) => strats.iter().flat_map(|s| s.types()).collect(), + Union(strats) => strats.iter().flat_map(|s| s.types()).collect(), + Filter(_, ty) => vec![ty.clone()], + } + } +} + +impl ToTokens for Strategy { + fn to_tokens(&self, tokens: &mut TokenStream) { + // The logic of each of these are pretty straight forward save for + // union which is described separately. + use self::Strategy::*; + match self { + Arbitrary(ty, span) => tokens.append_all(quote_spanned!(*span=> + <#ty as _proptest::arbitrary::Arbitrary>::Strategy + )), + Existential(ty) => quote_append!(tokens, + _proptest::strategy::BoxedStrategy<#ty> + ), + Value(ty) => quote_append!(tokens, fn() -> #ty ), + Map(strats) => { + let field_tys = self.types(); + let strats = strats.iter(); + quote_append!(tokens, + _proptest::strategy::Map< ( #(#strats,)* ), + fn( ( #(#field_tys,)* ) ) -> Self + > + ) + }, + Union(strats) => union_strat_to_tokens(tokens, strats), + Filter(strat, ty) => { + quote_append!(tokens, + _proptest::strategy::Filter<#strat, fn(&#ty) -> bool> + ) + }, + } + } +} + +//============================================================================== +// Constructor +//============================================================================== + +/// The right hand side (RHS) of a let binding of parameters. +pub enum FromReg { + /// Denotes a move from the top parameter given in the arguments of + /// `arbitrary_with`. + Top, + /// Denotes a move from a variable `params_` where `` is the given + /// number. + Num(usize), +} + +/// The left hand side (LHS) of a let binding of parameters. +pub enum ToReg { + /// Denotes a move and declaration to a sequence of variables from + /// `params_0` to `params_x`. + Range(usize), + /// Denotes a move and declaration of a special variable `params` that is + /// user facing and is ALWAYS named `params`. + /// + /// To change the name this linearises to is considered a breaking change + /// wrt. semver. + API, +} + +/// Models an expression that generates a proptest `Strategy`. +pub enum Ctor { + /// A strategy generated by using the `Arbitrary` impl for the given `Ty´. + /// If `Some(idx)` is specified, then a parameter at `params_` is used + /// and provided to `any_with::(params_)`. + Arbitrary(syn::Type, Option, Span), + /// An exact strategy value given by the expression. + Existential(syn::Expr), + /// A strategy that always produces the given expression. + Value(syn::Expr), + /// A strategy that always produces the given expression but which is erased. + ValueExistential(syn::Expr), + /// A strategy that maps from a sequence of strategies into `Self`. + Map(Box<[Ctor]>, MapClosure), + /// A strategy that randomly selects one of the given relative-weighted + /// strategies. + Union(Box<[(u32, Ctor)]>), + /// A let binding that moves to and declares the `ToReg` from the `FromReg` + /// as well as the strategy that uses the `ToReg`. + Extract(Box, ToReg, FromReg), + /// A filtered strategy with `.prop_filter`. + Filter(Box, syn::Expr), +} + +/// Wraps the given strategy producing expression with a move into +/// `params_` from `FromReg`. This is used when the given `c` expects +/// `params_` to be there. +pub fn extract_all(c: Ctor, to: usize, from: FromReg) -> Ctor { + extract(c, ToReg::Range(to), from) +} + +/// Wraps the given strategy producing expression with a move into `params` +/// (literally named like that) from `FromReg`. This is used when the given +/// `c` expects `params` to be there. +pub fn extract_api(c: Ctor, from: FromReg) -> Ctor { + extract(c, ToReg::API, from) +} + +impl ToTokens for FromReg { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + FromReg::Top => call_site_ident(TOP_PARAM_NAME).to_tokens(tokens), + FromReg::Num(reg) => param(*reg).to_tokens(tokens), + } + } +} + +impl ToTokens for ToReg { + fn to_tokens(&self, tokens: &mut TokenStream) { + match *self { + ToReg::Range(to) if to == 1 => param(0).to_tokens(tokens), + ToReg::Range(to) => Tuple((0..to).map(param)).to_tokens(tokens), + ToReg::API => call_site_ident(API_PARAM_NAME).to_tokens(tokens), + } + } +} + +impl ToTokens for Ctor { + fn to_tokens(&self, tokens: &mut TokenStream) { + // The logic of each of these are pretty straight forward save for + // union which is described separately. + use self::Ctor::*; + match self { + Filter(ctor, filter) => quote_append!(tokens, + _proptest::strategy::Strategy::prop_filter( + #ctor, stringify!(#filter), #filter) + ), + Extract(ctor, to, from) => quote_append!(tokens, { + let #to = #from; #ctor + }), + Arbitrary(ty, fv, span) => tokens.append_all(if let Some(fv) = fv { + let args = param(*fv); + quote_spanned!(*span=> + _proptest::arbitrary::any_with::<#ty>(#args) + ) + } else { + quote_spanned!(*span=> + _proptest::arbitrary::any::<#ty>() + ) + }), + Existential(expr) => quote_append!(tokens, + _proptest::strategy::Strategy::boxed( #expr ) ), + Value(expr) => quote_append!(tokens, || #expr ), + ValueExistential(expr) => quote_append!(tokens, + _proptest::strategy::Strategy::boxed( + _proptest::strategy::LazyJust::new(move || #expr) + ) + ), + Map(ctors, closure) => { + let ctors = ctors.iter(); + quote_append!(tokens, + _proptest::strategy::Strategy::prop_map( + ( #(#ctors,)* ), + #closure + ) + ); + }, + Union(ctors) => union_ctor_to_tokens(tokens, ctors), + } + } +} + +/// Tokenizes a weighted list of `Ctor`. +/// +/// The logic is that the output should be as linear as possible while still +/// supporting enums with an unbounded number of variants without any boxing +/// (erasure) or dynamic dispatch. +/// +/// As `TupleUnion` is (currently) limited to 10 summands in the coproduct we +/// can't just emit the entire thing linearly as this will fail on the 11:th +/// variant. +/// +/// A naive approach to solve might be to simply use a cons-list like so: +/// +/// ```ignore +/// TupleUnion::new( +/// (w_1, s_1), +/// (w_2 + w_3 + w_4 + w_5, +/// TupleUnion::new( +/// (w_2, s_2), +/// (w_3 + w_4 + w_5, +/// TupleUnion::new( +/// (w_3, s_3), +/// (w_4 + w_5, +/// TupleUnion::new( +/// (w_4, s_4), +/// (w_5, s_5), +/// )) +/// )) +/// )) +/// ) +/// ``` +/// +/// However, we can do better by being linear for the `10 - 1` first +/// strategies and then switch to nesting like so: +/// +/// ```ignore +/// (1, 2, 3, 4, 5, 6, 7, 8, 9, +/// (10, 11, 12, 13, 14, 15, 16, 17, 18, +/// (19, ..))) +/// ``` +fn union_ctor_to_tokens(tokens: &mut TokenStream, ctors: &[(u32, Ctor)]) { + if ctors.is_empty() { return; } + + if let [(_, ctor)] = ctors { + // This is not a union at all - user provided an enum with one variant. + ctor.to_tokens(tokens); + return; + } + + let mut chunks = ctors.chunks(UNION_CHUNK_SIZE); + let chunk = chunks.next().unwrap(); + let head = chunk.iter().map(|(w, c)| quote!( (#w, #c) )); + let tail = Recurse(weight_sum(ctors) - weight_sum(chunk), chunks); + + quote_append!(tokens, + _proptest::strategy::TupleUnion::new(( #(#head,)* #tail )) + ); + + struct Recurse<'a>(u32, ::std::slice::Chunks<'a, (u32, Ctor)>); + + impl<'a> ToTokens for Recurse<'a> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let (tweight, mut chunks) = (self.0, self.1.clone()); + + if let Some(chunk) = chunks.next() { + if let [(w, c)] = chunk { + // Only one element left - no need to nest. + quote_append!(tokens, (#w, #c) ); + } else { + let head = chunk.iter().map(|(w, c)| quote!( (#w, #c) )); + let tail = Recurse(tweight - weight_sum(chunk), chunks); + quote_append!(tokens, + (#tweight, _proptest::strategy::TupleUnion::new(( + #(#head,)* #tail + ))) + ); + } + } + } + } + + fn weight_sum(ctors: &[(u32, Ctor)]) -> u32 { + use std::num::Wrapping; + let Wrapping(x) = ctors.iter().map(|&(w, _)| Wrapping(w)).sum(); + x + } +} + +/// Tokenizes a weighted list of `Strategy`. +/// For details, see `union_ctor_to_tokens`. +fn union_strat_to_tokens(tokens: &mut TokenStream, strats: &[Strategy]) { + if strats.is_empty() { return; } + + if let [strat] = strats { + // This is not a union at all - user provided an enum with one variant. + strat.to_tokens(tokens); + return; + } + + let mut chunks = strats.chunks(UNION_CHUNK_SIZE); + let chunk = chunks.next().unwrap(); + let head = chunk.iter().map(|s| quote!( (u32, #s) )); + let tail = Recurse(chunks); + + quote_append!(tokens, + _proptest::strategy::TupleUnion<( #(#head,)* #tail )> + ); + + struct Recurse<'a>(::std::slice::Chunks<'a, Strategy>); + + impl<'a> ToTokens for Recurse<'a> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut chunks = self.0.clone(); + + if let Some(chunk) = chunks.next() { + if let [s] = chunk { + // Only one element left - no need to nest. + quote_append!(tokens, (u32, #s) ); + } else { + let head = chunk.iter().map(|s| quote!( (u32, #s) )); + let tail = Recurse(chunks); + quote_append!(tokens, + (u32, _proptest::strategy::TupleUnion<( + #(#head,)* #tail + )>) + ); + } + } + } + } +} + +/// Wraps a `Ctor` that expects the `to` "register" to be filled with +/// contents of the `from` register. The correctness of this wrt. the +/// generated Rust code has to be verified externally by checking the +/// construction of the particular `Ctor`. +fn extract(c: Ctor, to: ToReg, from: FromReg) -> Ctor { + Ctor::Extract(Box::new(c), to, from) +} + +/// Construct a `FreshVar` prefixed by `param_`. +fn param<'a>(fv: usize) -> FreshVar<'a> { + fresh_var("param", fv) +} + +//============================================================================== +// MapClosure +//============================================================================== + +/// Constructs a `MapClosure` for the given `path` and a list of fields. +pub fn map_closure(path: syn::Path, fs: &[syn::Field]) -> MapClosure { + MapClosure(path, fs.to_owned()) +} + +use syn::spanned::Spanned; + +/// A `MapClosure` models the closure part inside a `.prop_map(..)` call. +#[derive(Debug)] +pub struct MapClosure(syn::Path, Vec); + +impl ToTokens for MapClosure { + fn to_tokens(&self, tokens: &mut TokenStream) { + fn tmp_var<'a>(idx: usize) -> FreshVar<'a> { + fresh_var("tmp", idx) + } + + let MapClosure(path, fields) = self; + let count = fields.len(); + let tmps = (0..count).map(tmp_var); + let inits = fields.iter().enumerate().map(|(idx, field)| { + let tv = tmp_var(idx); + if let Some(name) = &field.ident { + quote_spanned!(field.span()=> #name: #tv ) + } else { + let name = syn::Member::Unnamed(syn::Index::from(idx)); + quote_spanned!(field.span()=> #name: #tv ) + } + }); + quote_append!(tokens, |( #(#tmps,)* )| #path { #(#inits),* } ); + } +} + +//============================================================================== +// FreshVar +//============================================================================== + +/// Construct a `FreshVar` with the given `prefix` and the number it has in the +/// count of temporaries for that prefix. +fn fresh_var(prefix: &str, count: usize) -> FreshVar { + FreshVar { prefix, count } +} + +/// A `FreshVar` is an internal implementation detail and models a temporary +/// variable on the stack. +struct FreshVar<'a> { + prefix: &'a str, + count: usize +} + +impl<'a> ToTokens for FreshVar<'a> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let ident = format!("{}_{}", self.prefix, self.count); + call_site_ident(&ident).to_tokens(tokens) + } +} + +fn call_site_ident(ident: &str) -> syn::Ident { + syn::Ident::new(ident, Span::call_site()) +} + +//============================================================================== +// Util +//============================================================================== + +/// A comma separated tuple to a token stream when more than 1, or just flat +/// when 1. +#[derive(Copy, Clone)] +struct Tuple2(S); + +impl<'a, T: ToTokens> ToTokens for Tuple2<&'a [T]> { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self.0 { + [x] => x.to_tokens(tokens), + _ => Tuple(self.0).to_tokens(tokens), + } + } +} + +/// Append a comma separated tuple to a token stream. +struct Tuple(I); + +impl> ToTokens for Tuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + let iter = self.0.clone(); + quote_append!(tokens, ( #(#iter),* ) ); + } +} diff --git a/proptest-derive/src/attr.rs b/proptest-derive/src/attr.rs new file mode 100644 index 00000000..6b27b17b --- /dev/null +++ b/proptest-derive/src/attr.rs @@ -0,0 +1,544 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Provides a parser from syn attributes to our logical model. + +use syn::{self, Meta, NestedMeta, Lit, Ident, Attribute, Expr, Type}; + +use util; +use interp; +use error::{self, Ctx, DeriveResult}; + +//============================================================================== +// Public API +//============================================================================== + +/// Parsed attributes in our logical model. +#[derive(Clone)] +pub struct ParsedAttributes { + /// If we've been ordered to skip this item. + /// This is only valid for enum variants. + pub skip: bool, + /// The potential weight assigned to an enum variant. + /// This must be `None` for things that are not enum variants. + pub weight: Option, + /// The mode for `Parameters` to use. See that type for more. + pub params: ParamsMode, + /// The mode for `Strategy` to use. See that type for more. + pub strategy: StratMode, + /// Filter expressions if any. + pub filter: Vec, + /// True if no_bound was specified. + pub no_bound: bool, +} + +/// The mode for the associated item `Strategy` to use. +#[derive(Clone)] +pub enum StratMode { + /// This means that no explicit strategy was specified + /// and that we thus should use `Arbitrary` for whatever + /// it is that needs a strategy. + Arbitrary, + /// This means that an explicit value has been provided. + /// The result of this is to use a strategy that always + /// returns the given value. + Value(Expr), + /// This means that an explicit strategy has been provided. + /// This strategy will be used to generate whatever it + /// is that the attribute was set on. + Strategy(Expr), +} + +/// The mode for the associated item `Parameters` to use. +#[derive(Clone)] +pub enum ParamsMode { + /// Nothing has been specified. The children are now free to + /// specify their parameters, and if nothing is specified, then + /// `::Parameters` will be used for a type `X`. + Passthrough, + /// We've been ordered to use the Default value of + /// `::Parameters` for some field where applicable. + /// For the top level item, this means that `Parameters` will be + /// the unit type. For children, it means that this child should + /// not count towards the product type that is being built up. + Default, + /// An explicit type has been specified on some item. + /// If the top level item has this specified on it, this means + /// that `Parameters` will have the given type. + /// If it is specified on a child of the top level item, this + /// entails that the given type will be added to the resultant + /// product type. + Specified(Type), +} + +impl ParamsMode { + /// Returns `true` iff the mode was explicitly set. + pub fn is_set(&self) -> bool { + if let ParamsMode::Passthrough = *self { false } else { true } + } + + /// Converts the mode to an `Option` of an `Option` of a type + /// where the outer `Option` is `None` iff the mode wasn't set + /// and the inner `Option` is `None` iff the mode was `Default`. + pub fn into_option(self) -> Option> { + use self::ParamsMode::*; + match self { + Passthrough => None, + Specified(ty) => Some(Some(ty)), + Default => Some(None), + } + } +} + +impl StratMode { + /// Returns `true` iff the mode was explicitly set. + pub fn is_set(&self) -> bool { + if let StratMode::Arbitrary = self { false } else { true } + } +} + +/// Parse the attributes specified on an item and parsed by syn +/// into our logical model that we work with. +pub fn parse_attributes(ctx: Ctx, attrs: &[Attribute]) + -> DeriveResult +{ + let attrs = parse_attributes_base(ctx, attrs)?; + if attrs.no_bound { + error::no_bound_set_on_non_tyvar(ctx); + } + Ok(attrs) +} + +/// Parse the attributes specified on a type definition... +pub fn parse_top_attributes(ctx: Ctx, attrs: &[Attribute]) + -> DeriveResult +{ + parse_attributes_base(ctx, attrs) +} + +/// Parses the attributes specified on an item and parsed by syn +/// and returns true if we've been ordered to not set an `Arbitrary` +/// bound on the given type variable the attributes are from, +/// no matter what. +pub fn has_no_bound(ctx: Ctx, attrs: &[Attribute]) -> DeriveResult { + let attrs = parse_attributes_base(ctx, attrs)?; + error::if_anything_specified(ctx, &attrs, error::TY_VAR); + Ok(attrs.no_bound) +} + +/// Parse the attributes specified on an item and parsed by syn +/// into our logical model that we work with. +fn parse_attributes_base(ctx: Ctx, attrs: &[Attribute]) + -> DeriveResult +{ + let acc = parse_accumulate(ctx, attrs); + + Ok(ParsedAttributes { + skip: acc.skip.is_some(), + weight: acc.weight, + filter: acc.filter, + // Process params and no_params together to see which one to use. + params: parse_params_mode(ctx, acc.no_params, acc.params)?, + // Process strategy and value together to see which one to use. + strategy: parse_strat_mode(ctx, acc.strategy, acc.value)?, + no_bound: acc.no_bound.is_some() + }) +} + +//============================================================================== +// Internals: Initialization +//============================================================================== + +#[derive(Default)] +struct ParseAcc { + skip: Option<()>, + weight: Option, + no_params: Option<()>, + params: Option, + strategy: Option, + value: Option, + filter: Vec, + no_bound: Option<()>, +} + +//============================================================================== +// Internals: Extraction & Filtering +//============================================================================== + +fn parse_accumulate(ctx: Ctx, attrs: &[Attribute]) -> ParseAcc { + let mut state = ParseAcc::default(); + + // Get rid of attributes we don't care about: + for attr in attrs { + if is_proptest_attr(&attr) { + // Flatten attributes so we deal with them uniformly. + state = extract_modifiers(ctx, &attr).into_iter() + // Accumulate attributes into a form for final processing. + .fold(state, |state, meta| dispatch_attribute(ctx, state, meta)) + } + } + + state +} + +/// Returns `true` iff the attribute has to do with proptest. +/// Otherwise, the attribute is irrevant to us and we will simply +/// ignore it in our processing. +fn is_proptest_attr(attr: &Attribute) -> bool { + util::eq_simple_path("proptest", &attr.path) +} + +/// Extract all individual attributes inside one `#[proptest(..)]`. +/// We do this to treat all pieces uniformly whether a single +/// `#[proptest(..)]` was used or many. This simplifies the +/// logic somewhat. +fn extract_modifiers(ctx: Ctx, attr: &Attribute) -> Vec { + // Ensure we've been given an outer attribute form. + if !is_outer_attr(&attr) { + error::inner_attr(ctx); + } + + match attr.interpret_meta() { + Some(Meta::List(list)) => return list.nested.into_iter() + .filter_map(|nm| extract_metas(ctx, nm)).collect(), + Some(Meta::Word(_)) => error::bare_proptest_attr(ctx), + Some(Meta::NameValue(_)) => error::literal_set_proptest(ctx), + None => error::no_interp_meta(ctx), + } + + vec![] +} + +fn extract_metas(ctx: Ctx, nested: NestedMeta) -> Option { + match nested { + NestedMeta::Literal(_) => { + error::immediate_literals(ctx); + None + }, + // This is the only valid form. + NestedMeta::Meta(meta) => Some(meta), + } +} + +/// Returns true iff the given attribute is an outer one, i.e: `#[]`. +/// An inner attribute is the other possibility and has the syntax `#![]`. +/// Note that `` is a meta-variable for the contents inside. +fn is_outer_attr(attr: &Attribute) -> bool { + syn::AttrStyle::Outer == attr.style +} + +//============================================================================== +// Internals: Dispatch +//============================================================================== + +/// Dispatches an attribute modifier to handlers and +/// let's them add stuff into our accumulartor. +fn dispatch_attribute(ctx: Ctx, mut acc: ParseAcc, meta: Meta) -> ParseAcc { + // Dispatch table for attributes: + let name = meta.name().to_string(); + let name = name.as_ref(); + match name { + // Valid modifiers: + "skip" => parse_skip(ctx, &mut acc, meta), + "w" | "weight" => parse_weight(ctx, &mut acc, &meta), + "no_params" => parse_no_params(ctx, &mut acc, meta), + "params" => parse_params(ctx, &mut acc, meta), + "strategy" => parse_strategy(ctx, &mut acc, &meta), + "value" => parse_value(ctx, &mut acc, &meta), + "filter" => parse_filter(ctx, &mut acc, &meta), + "no_bound" => parse_no_bound(ctx, &mut acc, meta), + // Invalid modifiers: + name => dispatch_unknown_mod(ctx, name), + } + acc +} + +fn dispatch_unknown_mod(ctx: Ctx, name: &str) { + match name { + "no_bounds" => + error::did_you_mean(ctx, name, "no_bound"), + "weights" | "weighted" => + error::did_you_mean(ctx, name, "weight"), + "strat" | "strategies" => + error::did_you_mean(ctx, name, "strategy"), + "values" | "valued" | "fix" | "fixed" => + error::did_you_mean(ctx, name, "value"), + "param" | "parameters" => + error::did_you_mean(ctx, name, "params"), + "no_param" | "no_parameters" => + error::did_you_mean(ctx, name, "no_params"), + name => + error::unkown_modifier(ctx, name), + // TODO: consider levenshtein distance. + } +} + +//============================================================================== +// Internals: no_bound +//============================================================================== + +/// Parse a no_bound attribute. +/// Valid forms are: +/// + `#[proptest(no_bound)]` +fn parse_no_bound(ctx: Ctx, acc: &mut ParseAcc, meta: Meta) { + parse_bare_modifier(ctx, &mut acc.no_bound, meta, error::no_bound_malformed) +} + +//============================================================================== +// Internals: Skip +//============================================================================== + +/// Parse a skip attribute. +/// Valid forms are: +/// + `#[proptest(skip)]` +fn parse_skip(ctx: Ctx, acc: &mut ParseAcc, meta: Meta) { + parse_bare_modifier(ctx, &mut acc.skip, meta, error::skip_malformed) +} + +//============================================================================== +// Internals: Weight +//============================================================================== + +/// Parses a weight. +/// Valid forms are: +/// + `#[proptest(weight = )]` +/// + `#[proptest(weight = "")]` +/// + `#[proptest(weight())]` +/// + `#[proptest(weight("""))]` +/// +/// The `` must also fit within an `u32` and be unsigned. +fn parse_weight(ctx: Ctx, acc: &mut ParseAcc, meta: &Meta) { + use std::u32; + error_if_set(ctx, &acc.weight, &meta); + + // Convert to value if possible: + let value = normalize_meta(meta.clone()) + .and_then(extract_lit) + .and_then(extract_expr) + // Evaluate the expression into a value: + .as_ref().and_then(interp::eval_expr) + // Ensure that `val` fits within an `u32` as proptest requires that: + .filter(|&value| value <= u128::from(u32::MAX)) + .map(|value| value as u32); + + if let Some(value) = value { + acc.weight = Some(value); + } else { + error::weight_malformed(ctx, meta) + } +} + +//============================================================================== +// Internals: Filter +//============================================================================== + +/// Parses an explicit value as a strategy. +/// Valid forms are: +/// + `#[proptest(filter())]` +/// + `#[proptest(filter = "")]` +/// + `#[proptest(filter("")]` +fn parse_filter(ctx: Ctx, acc: &mut ParseAcc, meta: &Meta) { + if let Some(filter) = match normalize_meta(meta.clone()) { + Some(NormMeta::Lit(Lit::Str(lit))) => lit.parse().ok(), + Some(NormMeta::Word(ident)) => Some(parse_quote!( #ident )), + _ => None, + } { + acc.filter.push(filter); + } else { + error::filter_malformed(ctx, meta) + } +} + +//============================================================================== +// Internals: Strategy +//============================================================================== + +/// Parses an explicit value as a strategy. +/// Valid forms are: +/// + `#[proptest(value = )]` +/// + `#[proptest(value = "")]` +/// + `#[proptest(value("")]` +/// + `#[proptest(value()]` +/// + `#[proptest(value()]` +fn parse_value(ctx: Ctx, acc: &mut ParseAcc, meta: &Meta) { + parse_strategy_base(ctx, &mut acc.value, meta) +} + +/// Parses an explicit strategy. +/// Valid forms are: +/// + `#[proptest(strategy = )]` +/// + `#[proptest(strategy = "")]` +/// + `#[proptest(strategy("")]` +/// + `#[proptest(strategy()]` +/// + `#[proptest(strategy()]` +fn parse_strategy(ctx: Ctx, acc: &mut ParseAcc, meta: &Meta) { + parse_strategy_base(ctx, &mut acc.strategy, meta) +} + +/// Parses an explicit strategy. This is a helper. +/// Valid forms are: +/// + `#[proptest( = )]` +/// + `#[proptest( = "")]` +/// + `#[proptest(("")]` +/// + `#[proptest(()]` +/// + `#[proptest(()]` +fn parse_strategy_base(ctx: Ctx, loc: &mut Option, meta: &Meta) { + error_if_set(ctx, &loc, &meta); + + if let Some(expr) = match normalize_meta(meta.clone()) { + Some(NormMeta::Lit(lit)) => extract_expr(lit), + Some(NormMeta::Word(fun)) => Some(parse_quote!( #fun() )), + _ => None, + } { + *loc = Some(expr); + } else { + error::strategy_malformed(ctx, meta) + } +} + +/// Combines any parsed explicit strategy and value into a single value +/// and fails if both an explicit strategy and value was set. +/// Only one of them can be set, or none. +fn parse_strat_mode(ctx: Ctx, strat: Option, value: Option) + -> DeriveResult +{ + Ok(match (strat, value) { + (None, None ) => StratMode::Arbitrary, + (None, Some(ty)) => StratMode::Value(ty), + (Some(ty), None ) => StratMode::Strategy(ty), + (Some(_), Some(_) ) => error::overspecified_strat(ctx)?, + }) +} + +//============================================================================== +// Internals: Parameters +//============================================================================== + +/// Combines a potentially set `params` and `no_params` into a single value +/// and fails if both have been set. Only one of them can be set, or none. +fn parse_params_mode(ctx: Ctx, no_params: Option<()>, ty_params: Option) + -> DeriveResult +{ + Ok(match (no_params, ty_params) { + (None, None ) => ParamsMode::Passthrough, + (None, Some(ty)) => ParamsMode::Specified(ty), + (Some(_), None ) => ParamsMode::Default, + (Some(_), Some(_) ) => error::overspecified_param(ctx)?, + }) +} + +/// Parses an explicit Parameters type. +/// +/// Valid forms are: +/// + `#[proptest(params()]` +/// + `#[proptest(params("")]` +/// + `#[proptest(params = ""]` +/// +/// The latter form is required for more complex types. +fn parse_params(ctx: Ctx, acc: &mut ParseAcc, meta: Meta) { + error_if_set(ctx, &acc.params, &meta); + + let typ = match normalize_meta(meta) { + // Form is: `#[proptest(params()]`. + Some(NormMeta::Word(ident)) => Some(ident_to_type(ident)), + // Form is: `#[proptest(params = ""]` or, + // Form is: `#[proptest(params("")]`.. + Some(NormMeta::Lit(Lit::Str(lit))) => lit.parse().ok(), + _ => None, + }; + + if let Some(typ) = typ { + acc.params = Some(typ); + } else { + error::param_malformed(ctx) + } +} + +/// Parses an order to use the default Parameters type and value. +/// Valid forms are: +/// + `#[proptest(no_params)]` +fn parse_no_params(ctx: Ctx, acc: &mut ParseAcc, meta: Meta) { + parse_bare_modifier(ctx, &mut acc.no_params, meta, error::no_params_malformed) +} + +//============================================================================== +// Internals: Utilities +//============================================================================== + +/// Parses a bare attribute of the form `#[proptest()]` and sets `loc`. +fn parse_bare_modifier + (ctx: Ctx, loc: &mut Option<()>, meta: Meta, malformed: fn(Ctx)) +{ + error_if_set(ctx, loc, &meta); + + if let Some(NormMeta::Plain) = normalize_meta(meta) { + *loc = Some(()); + } else { + malformed(ctx); + } +} + +/// Emits a "set again" error iff the given option `.is_some()`. +fn error_if_set(ctx: Ctx, loc: &Option, meta: &Meta) { + if loc.is_some() { + error::set_again(ctx, meta) + } +} + +/// Constructs a type out of an identifier. +fn ident_to_type(ident: Ident) -> Type { + Type::Path(syn::TypePath { qself: None, path: ident.into() }) +} + +/// Extract a `lit` in `NormMeta::Lit()`. +fn extract_lit(meta: NormMeta) -> Option { + if let NormMeta::Lit(lit) = meta { + Some(lit) + } else { + None + } +} + +/// Extract expression out of literal if possible. +fn extract_expr(lit: Lit) -> Option { + match lit { + Lit::Str(lit) => lit.parse().ok(), + Lit::Int(lit) => Some( + Expr::from(syn::ExprLit { attrs: vec![], lit: lit.into() }) + ), + _ => None, + } +} + +/// Normalized `Meta` into all the forms we will possibly accept. +#[derive(Debug)] +enum NormMeta { + /// Accepts: `#[proptest()]` + Plain, + /// Accepts: `#[proptest( = )]` and `#[proptest(())]` + Lit(Lit), + /// Accepts: `#[proptest(())`. + Word(Ident) +} + +/// Normalize a `meta: Meta` into the forms accepted in `#[proptest()]`. +fn normalize_meta(meta: Meta) -> Option { + Some(match meta { + Meta::Word(_) => NormMeta::Plain, + Meta::NameValue(nv) => NormMeta::Lit(nv.lit), + Meta::List(ml) => if let Some(nm) = util::match_singleton(ml.nested) { + match nm { + NestedMeta::Literal(lit) => NormMeta::Lit(lit), + NestedMeta::Meta(Meta::Word(word)) => NormMeta::Word(word), + _ => return None + } + } else { + return None + }, + }) +} diff --git a/proptest-derive/src/derive.rs b/proptest-derive/src/derive.rs new file mode 100644 index 00000000..46afdee7 --- /dev/null +++ b/proptest-derive/src/derive.rs @@ -0,0 +1,715 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Provides actual deriving logic for the crate. + +use proc_macro2::{TokenStream, Span}; +use syn::{Type, Path, Expr, Field, Ident, Variant, DeriveInput}; +use syn::spanned::Spanned; + +use util::{is_unit_type, self_ty, fields_to_vec}; +use void::IsUninhabited; +use error::{self, Ctx, Context, DeriveResult}; +use attr::{self, ParamsMode, ParsedAttributes, StratMode}; +use use_tracking::{UseMarkable, UseTracker}; +use ast::*; + +//============================================================================== +// API +//============================================================================== + +pub fn impl_proptest_arbitrary(ast: DeriveInput) -> TokenStream { + let mut ctx = Context::new(); + let result = derive_proptest_arbitrary(&mut ctx, ast); + match (result, ctx.check()) { + (Ok(derive), Ok(())) => derive, + (_, Err(err)) => err, + (Err(result), Ok(())) => + panic!("[proptest_derive]: internal error, this is a bug! \ + result: {:?}", result), + } +} + +/// Simplified version of `DeriveInput` from syn letting us be generic over +/// the body. +struct DeriveData { + ident: Ident, + attrs: ParsedAttributes, + tracker: UseTracker, + body: B +} + +/// Entry point for deriving `Arbitrary`. +fn derive_proptest_arbitrary(ctx: Ctx, ast: DeriveInput) + -> DeriveResult +{ + use syn::Data::*; + + // Deny lifetimes on type. + error::if_has_lifetimes(ctx, &ast); + + // Parse top level attributes: + let attrs = attr::parse_top_attributes(ctx, &ast.attrs)?; + + // Initialize tracker: + let mut tracker = UseTracker::new(ast.generics); + if attrs.no_bound { + tracker.no_track(); + } + + // Compile into our own high level IR for the impl: + let the_impl = match ast.data { + // Deal with structs: + Struct(data) => derive_struct(ctx, DeriveData { + tracker, attrs, ident: ast.ident, + body: fields_to_vec(data.fields), + }), + // Deal with enums: + Enum(data) => derive_enum(ctx, DeriveData { + tracker, attrs, ident: ast.ident, + body: data.variants.into_iter().collect(), + }), + // Unions are not supported: + _ => { error::not_struct_or_enum(ctx)? } + }?; + + // Linearise the IR into Rust code: + let q = the_impl.into_tokens(ctx)?; + + // We're done! + Ok(q) +} + +//============================================================================== +// Struct +//============================================================================== + +/// Entry point for deriving `Arbitrary` for `struct`s. +fn derive_struct(ctx: Ctx, mut ast: DeriveData>) -> DeriveResult { + // Deny attributes that are only for enum variants: + error::if_enum_attrs_present(ctx, &ast.attrs, error::STRUCT); + + // Deny an explicit strategy directly on the struct. + error::if_strategy_present(ctx, &ast.attrs, error::STRUCT); + + let v_path = ast.ident.clone().into(); + let parts = if ast.body.is_empty() { + // Deriving for a unit struct. + error::if_present_on_unit_struct(ctx, &ast.attrs); + let (strat, ctor) = pair_unit_self(&v_path); + (Params::empty(), strat, ctor) + } else { + // Not a unit struct. + + // Ensures that the fields of the given struct has fields which are all + // inhabited. If one field is uninhabited, the entire product type is + // uninhabited. + // + // A unit struct in the other branch is by definition always inhabited. + if (&*ast.body).is_uninhabited() { + error::uninhabited_struct(ctx); + } + + // Construct the closure for `.prop_map`: + let closure = map_closure(v_path, &ast.body); + + // The complexity of the logic depends mostly now on whether + // parameters were set directly on the type or not. + let parts = if let Some(param_ty) = ast.attrs.params.into_option() { + // Parameters was set on the struct itself, the logic is simpler. + add_top_params(param_ty, + derive_product_has_params(ctx, &mut ast.tracker, + error::STRUCT_FIELD, closure, ast.body)?) + } else { + // We need considerably more complex logic. + derive_product_no_params( + ctx, &mut ast.tracker, + ast.body, error::STRUCT_FIELD + )?.finish(closure) + }; + + // Possibly apply filter: + add_top_filter(ast.attrs.filter, parts) + }; + + // We're done! + Ok(Impl::new(ast.ident, ast.tracker, parts)) +} + +/// Apply the filter at the top level if provided. +fn add_top_filter(filter: Vec, parts: ImplParts) -> ImplParts { + let (params, strat, ctor) = parts; + let (strat, ctor) = add_filter_self(filter, (strat, ctor)); + (params, strat, ctor) +} + +/// Apply a filter with `Self` as the input type to the predicate. +fn add_filter_self(filter: Vec, pair: StratPair) -> StratPair { + pair_filter(filter, self_ty(), pair) +} + +/// Determine the `Parameters` part. We've already handled everything else. +/// After this, we have all parts needed for an impl. If `None` is given, +/// then the unit type `()` will be used for `Parameters`. +fn add_top_params(param_ty: Option, (strat, ctor): StratPair) -> ImplParts { + let params = Params::empty(); + if let Some(params_ty) = param_ty { + // We need to add `let params = _top;`. + (params + params_ty, strat, extract_api(ctor, FromReg::Top)) + } else { + (params, strat, ctor) + } +} + +/// Deriving for a list of fields (product type) on +/// which `params` or `no_params` was set directly. +fn derive_product_has_params( + ctx: Ctx, ut: &mut UseTracker, + item: &str, closure: MapClosure, fields: Vec) + -> DeriveResult +{ + // Fold into an accumulator of the strategy types and the expressions + // that produces the strategy. Finally turn the accumulator into + // a `.prop_map(..)` that produces the composite strategy. + let len = fields.len(); + fields.into_iter().try_fold(StratAcc::new(len), |acc, field| { + let attrs = attr::parse_attributes(ctx, &field.attrs)?; + + // Deny attributes that are only for enum variants: + error::if_enum_attrs_present(ctx, &attrs, item); + + // Deny setting parameters on the field since it has been set on parent: + error::if_specified_params(ctx, &attrs, item); + + // Determine the strategy for this field and add it to acc. + let span = field.span(); + let ty = field.ty.clone(); + let pair = product_handle_default_params(ut, ty, span, attrs.strategy); + let pair = pair_filter(attrs.filter, field.ty, pair); + Ok(acc.add(pair)) + }).map(|acc| acc.finish(closure)) +} + +/// Determine strategy using "Default" semantics for a product. +fn product_handle_default_params + (ut: &mut UseTracker, ty: Type, span: Span, strategy: StratMode) -> StratPair { + match strategy { + // Specific strategy - use the given expr and erase the type + // (since we don't know about it): + StratMode::Strategy(strat) => pair_existential(ty, strat), + // Specific value - use the given expr: + StratMode::Value(value) => pair_value(ty, value), + // Use Arbitrary for the given type and mark the type as used: + StratMode::Arbitrary => { ty.mark_uses(ut); pair_any(ty, span) }, + } +} + +/// Deriving for a list of fields (product type) on +/// which `params` or `no_params` was NOT set directly. +fn derive_product_no_params + (ctx: Ctx, ut: &mut UseTracker, fields: Vec, item: &str) + -> DeriveResult> +{ + // Fold into an accumulator of the strategy types and the expressions + // that produces the strategy. We then just return that accumulator + // and let the caller of this function determine what to do with it. + let acc = PartsAcc::new(fields.len()); + fields.into_iter().try_fold(acc, |mut acc, field| { + let attrs = attr::parse_attributes(ctx, &field.attrs)?; + + // Deny attributes that are only for enum variants: + error::if_enum_attrs_present(ctx, &attrs, item); + + let span = field.span(); + let ty = field.ty; + + let strat = pair_filter(attrs.filter, ty.clone(), match attrs.params { + // Parameters were not set on the field: + ParamsMode::Passthrough => match attrs.strategy { + // Specific strategy - use the given expr and erase the type: + StratMode::Strategy(strat) => pair_existential(ty, strat), + // Specific value - use the given expr: + StratMode::Value(value) => pair_value(ty, value), + // Use Arbitrary for the given type and mark the type as used: + StratMode::Arbitrary => { + ty.mark_uses(ut); + + // We use the Parameters type of the field's type. + let pref = acc.add_param(arbitrary_param(&ty)); + pair_any_with(ty, pref, span) + }, + }, + // no_params set on the field: + ParamsMode::Default => + product_handle_default_params(ut, ty, span, attrs.strategy), + // params() set on the field: + ParamsMode::Specified(params_ty) => match attrs.strategy { + // Specific strategy - use the given expr and erase the type: + StratMode::Strategy(strat) => + // We need to extract the param as the binding `params`: + extract_nparam(&mut acc, params_ty, + pair_existential(ty, strat)), + // Specific value - use the given expr in a closure and erase: + StratMode::Value(value) => + extract_nparam(&mut acc, params_ty, + pair_value_exist(ty, value)), + // Logic error by user. Pointless to specify params and not + // the strategy. Bail! + StratMode::Arbitrary => + error::cant_set_param_but_not_strat(ctx, &ty, item)?, + }, + }); + Ok(acc.add_strat(strat)) + }) +} + +/// Wrap the given constructor with a let binding +/// moving `param_` into `params`. +fn extract_nparam + (acc: &mut PartsAcc, params_ty: Type, (strat, ctor): StratPair) + -> StratPair +{ + (strat, extract_api(ctor, FromReg::Num(acc.add_param(params_ty)))) +} + +//============================================================================== +// Enum +//============================================================================== + +/// Entry point for deriving `Arbitrary` for `enum`s. +fn derive_enum(ctx: Ctx, mut ast: DeriveData>) -> DeriveResult { + use void::IsUninhabited; + + // An enum can't be skipped, ensure it hasn't been: + error::if_skip_present(ctx, &ast.attrs, error::ENUM); + + // We don't allow a strategy on the enum directly: + error::if_strategy_present(ctx, &ast.attrs, error::ENUM); + + // We don't allow weight on enums directly: + error::if_weight_present(ctx, &ast.attrs, error::ENUM); + + // Bail if there are no variants: + if ast.body.is_empty() { + error::uninhabited_enum_with_no_variants(ctx)?; + } + + // Bail if all variants are uninhabited: + if (&*ast.body).is_uninhabited() { + error::uninhabited_enum_variants_uninhabited(ctx)?; + } + + // The complexity of the logic depends mostly now on whether + // parameters were set directly on the type or not. + let parts = if let Some(sty) = ast.attrs.params.into_option() { + // The logic is much simpler in this branch. + derive_enum_has_params(ctx, &mut ast.tracker, &ast.ident, ast.body, sty) + } else { + // And considerably more complex here. + derive_enum_no_params(ctx, &mut ast.tracker, &ast.ident, ast.body) + }?; + + let parts = add_top_filter(ast.attrs.filter, parts); + + // We're done! + Ok(Impl::new(ast.ident, ast.tracker, parts)) +} + +/// Deriving for a enum on which `params` or `no_params` was NOT set directly. +fn derive_enum_no_params( + ctx: Ctx, ut: &mut UseTracker, _self: &Ident, variants: Vec) + -> DeriveResult +{ + // Initialize the accumulator: + let mut acc = PartsAcc::new(variants.len()); + + // Fold into the accumulator the strategies for each variant: + for variant in variants { + if let Some((weight, ident, fields, attrs)) + = keep_inhabited_variant(ctx, _self, variant)? { + let path = parse_quote!( #_self::#ident ); + let (strat, ctor) = if fields.is_empty() { + // Unit variant: + pair_unit_variant(ctx, &attrs, path) + } else { + // Not a unit variant: + derive_variant_with_fields(ctx, ut, path, attrs, fields, &mut acc)? + }; + acc = acc.add_strat((strat, (weight, ctor))); + } + } + + ensure_union_has_strategies(ctx, &acc.strats); + + // Package the strategies into a union. + Ok(acc.finish(ctx)) +} + +/// Ensure that there's at least one generatable variant for a union. +fn ensure_union_has_strategies(ctx: Ctx, strats: &StratAcc) { + if strats.is_empty() { + // We didn't accumulate any strategies, + // so we can't construct any variant. + error::uninhabited_enum_because_of_skipped_variants(ctx); + } +} + +/// Derive for a variant which has fields and where the +/// variant or its fields may specify `params` or `no_params`. +fn derive_variant_with_fields + (ctx: Ctx, ut: &mut UseTracker, v_path: Path, attrs: ParsedAttributes, + fields: Vec, acc: &mut PartsAcc) + -> DeriveResult +{ + let filter = attrs.filter.clone(); + + let pair = match attrs.params { + // Parameters were not set on the variant: + ParamsMode::Passthrough => match attrs.strategy { + // Specific strategy - use the given expr and erase the type: + StratMode::Strategy(strat) => { + deny_all_attrs_on_fields(ctx, fields)?; + pair_existential_self(strat) + }, + // Specific value - use the given expr: + StratMode::Value(value) => { + deny_all_attrs_on_fields(ctx, fields)?; + pair_value_self(value) + }, + // Use Arbitrary for the factors (fields) of variant: + StratMode::Arbitrary => { + // Compute parts for the inner product: + let closure = map_closure(v_path, &fields); + let fields_acc = derive_product_no_params(ctx, ut, fields, + error::ENUM_VARIANT_FIELD)?; + let (params, count) = fields_acc.params.consume(); + let (strat, ctor) = fields_acc.strats.finish(closure); + + // Add params types from inner derive as a single type + // in the outer params types. + let params_ty = params.into(); + (strat, if is_unit_type(¶ms_ty) { ctor } else { + let pref = acc.add_param(params_ty); + if pref + 1 == count { + ctor + } else { + extract_all(ctor, count, FromReg::Num(pref)) + } + }) + }, + }, + // no_params set on the variant: + ParamsMode::Default => + variant_handle_default_params(ctx, ut, v_path, attrs, fields)?, + // params() set on the variant: + ParamsMode::Specified(params_ty) => match attrs.strategy { + // Specific strategy - use the given expr and erase the type: + StratMode::Strategy(strat) => { + deny_all_attrs_on_fields(ctx, fields)?; + extract_nparam(acc, params_ty, pair_existential_self(strat)) + }, + // Specific value - use the given expr in a closure and erase: + StratMode::Value(value) => { + deny_all_attrs_on_fields(ctx, fields)?; + extract_nparam(acc, params_ty, pair_value_exist_self(value)) + }, + // Logic error by user. Pointless to specify params and not + // the strategy. Bail! + StratMode::Arbitrary => { + let ty = self_ty(); + error::cant_set_param_but_not_strat(ctx, &ty, error::ENUM_VARIANT)? + }, + }, + }; + let pair = add_filter_self(filter, pair); + Ok(pair) +} + +/// Determine strategy using "Default" semantics for a variant. +fn variant_handle_default_params( + ctx: Ctx, ut: &mut UseTracker, + v_path: Path, attrs: ParsedAttributes, fields: Vec) + -> DeriveResult { + let pair = match attrs.strategy { + // Specific strategy - use the given expr and erase the type: + StratMode::Strategy(strat) => { + deny_all_attrs_on_fields(ctx, fields)?; + pair_existential_self(strat) + }, + // Specific value - use the given expr: + StratMode::Value(value) => { + deny_all_attrs_on_fields(ctx, fields)?; + pair_value_self(value) + }, + // Use Arbitrary for the factors (fields) of variant: + StratMode::Arbitrary => + // Fields are not allowed to specify params. + derive_product_has_params(ctx, ut, error::ENUM_VARIANT_FIELD, + map_closure(v_path, &fields), fields)?, + }; + + Ok(pair) +} + +/// Ensures that there are no proptest attributes on any of the fields. +fn deny_all_attrs_on_fields(ctx: Ctx, fields: Vec) -> DeriveResult<()> { + fields.into_iter().try_for_each(|field| { + let f_attr = attr::parse_attributes(ctx, &field.attrs)?; + error::if_anything_specified(ctx, &f_attr, error::ENUM_VARIANT_FIELD); + Ok(()) + }) +} + +/// Derive for a variant which has fields and where the +/// variant or its fields may NOT specify `params` or `no_params`. +fn derive_enum_has_params( + ctx: Ctx, ut: &mut UseTracker, _self: &Ident, variants: Vec, + sty: Option) + -> DeriveResult +{ + // Initialize the accumulator: + let mut acc = StratAcc::new(variants.len()); + + // Fold into the accumulator the strategies for each variant: + for variant in variants { + let parts = keep_inhabited_variant(ctx, _self, variant)?; + if let Some((weight, ident, fields, attrs)) = parts { + let path = parse_quote!( #_self::#ident ); + let (strat, ctor) = if fields.is_empty() { + // Unit variant: + pair_unit_variant(ctx, &attrs, path) + } else { + // Not a unit variant: + let filter = attrs.filter.clone(); + add_filter_self(filter, + variant_handle_default_params(ctx, ut, path, attrs, fields)?) + }; + acc = acc.add((strat, (weight, ctor))); + } + } + + ensure_union_has_strategies(ctx, &acc); + + Ok(add_top_params(sty, acc.finish(ctx))) +} + +/// Filters out uninhabited and variants that we've been ordered to skip. +fn keep_inhabited_variant(ctx: Ctx, _self: &Ident, variant: Variant) + -> DeriveResult, ParsedAttributes)>> +{ + use void::IsUninhabited; + + let attrs = attr::parse_attributes(ctx, &variant.attrs)?; + let fields = fields_to_vec(variant.fields); + + if attrs.skip { + // We've been ordered to skip this variant! + // Check that all other attributes are not set. + ensure_has_only_skip_attr(ctx, &attrs, error::ENUM_VARIANT); + fields.into_iter().try_for_each(|field| { + let f_attrs = attr::parse_attributes(ctx, &field.attrs)?; + error::if_skip_present(ctx, &f_attrs, error::ENUM_VARIANT_FIELD); + ensure_has_only_skip_attr(ctx, &f_attrs, error::ENUM_VARIANT_FIELD); + Ok(()) + })?; + + return Ok(None) + } + + // If the variant is uninhabited, we can't generate it, so skip it. + if (&*fields).is_uninhabited() { return Ok(None) } + + // Compute the weight: + let weight = attrs.weight.unwrap_or(1); + + Ok(Some((weight, variant.ident, fields, attrs))) +} + +/// Ensures that no other attributes than skip are present. +fn ensure_has_only_skip_attr(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if attrs.params.is_set() { + error::skipped_variant_has_param(ctx, item); + } + + if attrs.strategy.is_set() { + error::skipped_variant_has_strat(ctx, item); + } + + if attrs.weight.is_some() { + error::skipped_variant_has_weight(ctx, item); + } + + if !attrs.filter.is_empty() { + error::skipped_variant_has_filter(ctx, item); + } +} + +/// Deal with a unit variant. +fn pair_unit_variant(ctx: Ctx, attrs: &ParsedAttributes, v_path: Path) + -> StratPair +{ + error::if_present_on_unit_variant(ctx, attrs); + pair_unit_self(&v_path) +} + +//============================================================================== +// Combined accumulator +//============================================================================== + +/// Combined accumulator for the parameters and strategies. +struct PartsAcc { + /// The accumulator for the parameters. + params: ParamAcc, + /// The accumulator for the strategies. + strats: StratAcc, +} + +impl PartsAcc { + /// Constructs a new accumulator with the size + /// passed on to the accumulator for the strategies. + fn new(size: usize) -> Self { + Self { + params: ParamAcc::empty(), + strats: StratAcc::new(size), + } + } + + /// Adds a strategy to the accumulator. + fn add_strat(self, pair: (Strategy, C)) -> Self { + Self { + strats: self.strats.add(pair), + params: self.params + } + } + + /// Adds a parameter type to the accumulator and returns how many types + /// there were before adding. + fn add_param(&mut self, ty: Type) -> usize { + self.params.add(ty) + } +} + +impl PartsAcc { + /// Finishes off the accumulator by returning the parts needed for + /// deriving. The resulting strategy is a mapping of the parts into + /// the `Self` type. + fn finish(self, closure: MapClosure) -> ImplParts { + let (params, count) = self.params.consume(); + let (strat, ctor) = self.strats.finish(closure); + (params, strat, extract_all(ctor, count, FromReg::Top)) + } +} + +impl PartsAcc<(u32, Ctor)> { + /// Finishes off the accumulator by returning the parts needed for + /// deriving. The resultant strategy is one that randomly picks + /// one of the parts based on the relative weights in the `u32`. + fn finish(self, ctx: Ctx) -> ImplParts { + let (params, count) = self.params.consume(); + let (strat, ctor) = self.strats.finish(ctx); + (params, strat, extract_all(ctor, count, FromReg::Top)) + } +} + +//============================================================================== +// Param accumulator +//============================================================================== + +/// Accumulator of the parameter types. +struct ParamAcc { + /// The accumulated parameters types. + types: Params, +} + +impl ParamAcc { + /// Returns an empty accumulator. + fn empty() -> Self { + Self { types: Params::empty(), } + } + + /// Adds a type to the accumulator and returns the type count before adding. + fn add(&mut self, ty: Type) -> usize { + let var = self.types.len(); + self.types += ty; + var + } + + /// Consumes the accumulator returning the types and the count. + fn consume(self) -> (Params, usize) { + let count = self.types.len(); + (self.types, count) + } +} + +//============================================================================== +// Strategy accumulator +//============================================================================== + +/// Accumulator of a sequence of strategies (both type and constructor). +struct StratAcc { + /// The type half of the accumulator: + types: Vec, + /// The constructors (Rust expression that makes the strategy) half: + ctors: Vec, +} + +impl StratAcc { + /// Construct the given accumulator with + /// initial capacity according to `size`. + fn new(size: usize) -> Self { + Self { + types: Vec::with_capacity(size), + ctors: Vec::with_capacity(size), + } + } + + /// Add the given type and constructor pair to + /// the accumulator which is moved and returned. + fn add(mut self, (strat, ctor): (Strategy, C)) -> Self { + self.types.push(strat); + self.ctors.push(ctor); + self + } + + /// Consume the accumulator returning the: + /// + sequence of strategies + /// + sequence of constructors + fn consume(self) -> (Vec, Vec) { + (self.types, self.ctors) + } + + /// Returns `true` iff nothing has been accumulated yet. + fn is_empty(&self) -> bool { + self.types.is_empty() + } +} + +impl StratAcc { + /// Finishes off the accumulator by returning + /// a `.prop_map()` of the strategies. + fn finish(self, closure: MapClosure) -> StratPair { + pair_map(self.consume(), closure) + } +} + +impl StratAcc<(u32, Ctor)> { + /// Finishes off the accumulator by returning a union of the + /// strategies where the resultant strategy randomly picks + /// one of the summands based on the relative weights provided. + fn finish(self, ctx: Ctx) -> StratPair { + // Check that the weight sum <= u32::MAX + if self.ctors.iter() + .map(|&(w, _)| w) + .try_fold(0u32, |acc, w| acc.checked_add(w)) + .is_none() { + error::weight_overflowing(ctx) + } + + pair_oneof(self.consume()) + } +} \ No newline at end of file diff --git a/proptest-derive/src/error.rs b/proptest-derive/src/error.rs new file mode 100644 index 00000000..403eafe0 --- /dev/null +++ b/proptest-derive/src/error.rs @@ -0,0 +1,512 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Provides error messages and some checkers + +use proc_macro2::TokenStream; + +use syn; +use attr::ParsedAttributes; + +//============================================================================== +// Item descriptions +//============================================================================== + +/// Item name of structs. +pub const STRUCT: &str = "struct"; + +/// Item name of struct fields. +pub const STRUCT_FIELD: &str = "struct field"; + +/// Item name of enums. +pub const ENUM: &str = "enum"; + +/// Item name of enum variants. +pub const ENUM_VARIANT: &str = "enum variant"; + +/// Item name of enum variant fields. +pub const ENUM_VARIANT_FIELD: &str = "enum variant field"; + +/// Item name for a type variable. +pub const TY_VAR: &str = "a type variable"; + +//============================================================================== +// Checkers +//============================================================================== + +/// Ensures that the type is not parametric over lifetimes. +pub fn if_has_lifetimes(ctx: Ctx, ast: &syn::DeriveInput) { + if ast.generics.lifetimes().count() > 0 { + has_lifetimes(ctx); + } +} + +/// Ensures that no attributes were specified on `item`. +pub fn if_anything_specified(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if_enum_attrs_present(ctx, attrs, item); + if_strategy_present(ctx, attrs, item); + if_specified_params(ctx, attrs, item); + if_specified_filter(ctx, attrs, item); +} + +/// Ensures that things only allowed on an enum variant is not present on +/// `item` which is not an enum variant. +pub fn if_enum_attrs_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if_skip_present(ctx, attrs, item); + if_weight_present(ctx, attrs, item); +} + +/// Ensures that parameters is not present on `item`. +pub fn if_specified_filter(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if !attrs.filter.is_empty() { parent_has_filter(ctx, item); } +} + +/// Ensures that parameters is not present on `item`. +pub fn if_specified_params(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if attrs.params.is_set() { parent_has_param(ctx, item); } +} + +/// Ensures that an explicit strategy or value is not present on `item`. +pub fn if_strategy_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + use attr::StratMode::*; + match attrs.strategy { + Arbitrary => {}, + Strategy(_) => illegal_strategy(ctx, "strategy", item), + Value(_) => illegal_strategy(ctx, "value", item), + } +} + +/// Ensures that a strategy, value, params, filter is not present on a unit variant. +pub fn if_present_on_unit_variant(ctx: Ctx, attrs: &ParsedAttributes) { + /// Ensures that an explicit strategy or value is not present on a unit variant. + use attr::StratMode::*; + match attrs.strategy { + Arbitrary => {}, + Strategy(_) => strategy_on_unit_variant(ctx, "strategy"), + Value(_) => strategy_on_unit_variant(ctx, "value"), + } + + if attrs.params.is_set() { + params_on_unit_variant(ctx) + } + + if !attrs.filter.is_empty() { + filter_on_unit_variant(ctx) + } +} + +/// Ensures that parameters or filter is not present on a unit struct. +pub fn if_present_on_unit_struct(ctx: Ctx, attrs: &ParsedAttributes) { + if attrs.params.is_set() { + params_on_unit_struct(ctx) + } + + if !attrs.filter.is_empty() { + filter_on_unit_struct(ctx) + } +} + +/// Ensures that skip is not present on `item`. +pub fn if_skip_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if attrs.skip { + illegal_skip(ctx, item) + } +} + +/// Ensures that a weight is not present on `item`. +pub fn if_weight_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) { + if attrs.weight.is_some() { + illegal_weight(ctx, item) + } +} + +//============================================================================== +// Messages +//============================================================================== + +use std::fmt::Display; + +#[derive(Debug)] +pub struct Fatal; +pub type DeriveResult = Result; +pub type Ctx<'ctx> = &'ctx mut Context; + +pub struct Context { + errors: Vec, +} + +impl Context { + pub fn new() -> Self { + Self { + errors: Vec::new(), + } + } + + pub fn error(&mut self, msg: T) { + self.errors.push(msg.to_string()); + } + + pub fn fatal(&mut self, msg: T) -> DeriveResult { + self.error(msg); + Err(Fatal) + } + + pub fn check(mut self) -> Result<(), TokenStream> { + fn compile_error(msg: &str) -> TokenStream { + quote! { + compile_error!(#msg); + } + } + + match self.errors.len() { + 0 => Ok(()), + 1 => Err(compile_error(&self.errors.pop().unwrap())), + n => { + let mut msg = format!("{} errors:", n); + for err in self.errors { + msg.push_str("\n\t# "); + msg.push_str(&err); + } + Err(compile_error(&msg)) + } + } + } +} + +//============================================================================== +// Messages +//============================================================================== + +macro_rules! mk_err_msg { + ($code: ident, $msg: expr) => { + concat!( + "[proptest_derive, ", stringify!($code), "]", + " during #[derive(Arbitrary)]:\n", + $msg, + " Please see: https://PATH/TO/foo#", stringify!($code), + " for more information.") + } +} + +/// A macro constructing errors that do not halt compilation immediately. +macro_rules! fatal { + ($error: ident, $code: ident, $msg: expr) => { + pub fn $error(ctx: Ctx) -> DeriveResult { + ctx.fatal(mk_err_msg!($code, $msg)) + } + }; + ($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident, + $msg: expr, $($fmt: tt)+) => { + pub fn $error(ctx: Ctx, $($arg: $arg_ty),*) -> DeriveResult { + ctx.fatal(format!(mk_err_msg!($code, $msg), $($fmt)+)) + } + }; +} + +/// A macro constructing fatal errors that do halt compilation immediately. +macro_rules! error { + ($error: ident, $code: ident, $msg: expr) => { + pub fn $error(ctx: Ctx) { + ctx.error(mk_err_msg!($code, $msg)) + } + }; + ($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident, + $msg: expr, $($fmt: tt)+) => { + pub fn $error(ctx: Ctx, $($arg: $arg_ty),*) { + ctx.error(format!(mk_err_msg!($code, $msg), $($fmt)+)) + } + }; +} + +/// Happens when we've been asked to derive `Arbitrary` for a type +/// that is parametric over lifetimes. Since proptest does not support +/// such types (yet), neither can we. +error!(has_lifetimes, E0001, + "Can't derive `Arbitrary` for types with generic lifetimes, such as: \ + `struct Foo<'a> { bar: &'a str }`. Currently, strategies for such types \ + are impossible to define."); + +/// Happens when we've been asked to derive `Arbitrary` for something +/// that is neither an enum nor a struct. Most likely, we've been given +/// a union type. This might be supported in the future, but not yet. +fatal!(not_struct_or_enum, E0002, + "Deriving is only possible for structs and enums. \ + It is currently not defined unions."); + +/// Happens when a struct has at least one field that is uninhabited. +/// There must at least exist one variant that we can construct. +error!(uninhabited_struct, E0003, + "The struct you are deriving `Arbitrary` for is uninhabited since one of \ + its fields is uninhabited. An uninhabited type is by definition impossible \ + to generate."); + +/// Happens when an enum has zero variants. Such an enum is obviously +/// uninhabited and can not be constructed. There must at least exist +/// one variant that we can construct. +fatal!(uninhabited_enum_with_no_variants, E0004, + "The enum you are deriving `Arbitrary` for is uninhabited since it has no \ + variants. An example of such an `enum` is: `enum Void {}`. \ + An uninhabited type is by definition impossible to generate."); + +/// Happens when an enum is uninhabited due all its variants being +/// uninhabited (why has the user given us such a weird enum?.. +/// Nonetheless, we do our best to ensure soundness). +/// There must at least exist one variant that we can construct. +fatal!(uninhabited_enum_variants_uninhabited, E0005, + "The enum you are deriving `Arbitrary` for is uninhabited since all its \ + variants are uninhabited. \ + An uninhabited type is by definition impossible to generate."); + +/// Happens when an enum becomes effectively uninhabited due +/// to all inhabited variants having been skipped. There must +/// at least exist one variant that we can construct. +error!(uninhabited_enum_because_of_skipped_variants, E0006, + "The enum you are deriving `Arbitrary` for is uninhabited for all intents \ + and purposes since you have `#[proptest(skip)]`ed all inhabited variants. \ + An uninhabited type is by definition impossible to generate."); + +/// Happens when `#[proptest(strategy = "")]` or +/// `#[proptest(value = "")]` is specified on an `item` +/// that does not support setting an explicit value or strategy. +/// An enum or struct does not support that. +error!(illegal_strategy(attr: &str, item: &str), E0007, + "`#[proptest({0} = \"\")]` is not allowed on {1}. Only struct fields, \ + enum variants and fields inside those can use an explicit {0}.", + attr, item); + +/// Happens when `#[proptest(skip)]` is specified on an `item` that does +/// not support skipping. Only enum variants support skipping. +error!(illegal_skip(item: &str), E0008, + "A {} can't be `#[proptest(skip)]`ed, only enum variants can be skipped.", + item); + +/// Happens when `#[proptest(weight = )]` is specified on an +/// `item` that does not support weighting. +error!(illegal_weight(item: &str), E0009, + "`#[proptest(weight = )]` is not allowed on {} as it is \ + meaningless. Only enum variants can be assigned weights.", + item); + +/// Happens when `#[proptest(params = )]` is set on `item` +/// but also on the parent of `item`. If the parent has set `params` +/// then that applies, and the `params` on `item` would be meaningless +/// wherefore it is forbidden. +error!(parent_has_param(item: &str), E0010, + "Can not set the associated type `Parameters` of `Arbitrary` with either \ + `#[proptest(no_params)]` or `#[proptest(params()]` on {} since it \ + was set on the parent.", + item); + +/// Happens when `#[proptest(params = )]` is set on `item` +/// but not `#[proptest(strategy = )]`. +/// This does not apply to the top level type declaration. +fatal!(cant_set_param_but_not_strat(self_ty: &syn::Type, item: &str), E0011, + "Can not set `#[proptest(params = )]` on {0} while not providing a \ + strategy for the {0} to use it since `<{1} as Arbitrary<'a>>::Strategy` \ + may require a different type than the one provided in ``.", + item, quote! { #self_ty }); + +/// Happens when `#[proptest(filter = "")]` is set on `item` +/// but also on the parent of `item`. If the parent has set `filter` +/// then that applies, and the `filter` on `item` would be meaningless +/// wherefore it is forbidden. +error!(parent_has_filter(item: &str), E0012, + "Can not set `#[proptest(filter(..)]` on {} since it is set on the variant + which it is inside of and because the variant specifies how to generate + itself.", + item); + +/// Happens when the form `#![proptest<..>]` is used. This will probably never +/// happen - but just in case it does, we catch it and emit an error. +error!(inner_attr, E0013, + "Inner attributes `#![proptest(..)]` are not currently supported."); + +/// Happens when the form `#[proptest]` is used. The form contains no +/// information for us to process, so we disallow it. +error!(bare_proptest_attr, E0014, + "Bare `#[proptest]` attributes are not allowed."); + +/// Happens when the form `#[proptest = )]` is used. +/// Only the form `#[proptest()]` is supported. +error!(literal_set_proptest, E0015, + "The attribute form `#[proptest = ]` is not allowed."); + +/// Happens when `` in `#[proptest()]` is a literal and +/// not a real modifier. +error!(immediate_literals, E0016, + "Literals immediately inside `#[proptest(..)]` as in \ + `#[proptest(, ..)]` are not allowed."); + +/// Happens when `` in `#[proptest()]` is set more than +/// once. +error!(set_again(meta: &syn::Meta), E0017, + "The attribute modifier `{}` inside `#[proptest(..)]` has already been \ + set. To fix the error, please remove at least one such modifier.", + meta.name()); + +/// Happens when `` in `#[proptest()]` is unknown to +/// us but we can make an educated guess as to what the user meant. +error!(did_you_mean(found: &str, expected: &str), E0018, + "Unknown attribute modifier `{}` inside #[proptest(..)] is not allowed. \ + Did you mean to use `{}` instead?", + found, expected); + +/// Happens when `` in `#[proptest()]` is unknown to us. +error!(unkown_modifier(modifier: &str), E0018, + "Unknown attribute modifier `{}` inside `#[proptest(..)]` is not allowed.", + modifier); + +/// Happens when `#[proptest(no_params)]` is malformed. +error!(no_params_malformed, E0019, + "The attribute modifier `no_params` inside `#[proptest(..)]` does not \ + support any further configuration and must be a plain modifier as in \ + `#[proptest(no_params)]`."); + +/// Happens when `#[proptest(skip)]` is malformed. +error!(skip_malformed, E0020, + "The attribute modifier `skip` inside `#[proptest(..)]` does not support \ + any further configuration and must be a plain modifier as in \ + `#[proptest(skip)]`."); + +/// Happens when `#[proptest(weight..)]` is malformed. +error!(weight_malformed(meta: &syn::Meta), E0021, + "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \ + format `#[proptest({0} = )]` where `` is an integer that \ + fits within a `u32`. An example: `#[proptest({0} = 2)]` to set a relative \ + weight of 2.", + meta.name()); + +/// Happens when both `#[proptest(params = "")]` and +/// `#[proptest(no_params)]` were specified. They are mutually +/// exclusive choices. The user can resolve this by picking one. +fatal!(overspecified_param, E0022, + "Can not set `#[proptest(no_params)]` as well as \ + `#[proptest(params())]` simultaneously. \ + Please pick one of those attributes."); + +/// This happens when `#[proptest(params..)]` is malformed. +/// For example, `#[proptest(params)]` is malformed. Another example is when +/// `` inside `#[proptest(params = "")]` or +/// `#[proptest(params(""))]` is malformed. In other words, `` is +/// not a valid Rust type. Note that `syn` may not cover all valid Rust types. +error!(param_malformed, E0023, + "The attribute modifier `params` inside #[proptest(..)] must have the \ + format `#[proptest(params = \"\")]` where `` is a valid type \ + in Rust. An example: `#[proptest(params = \"ComplexType\")]`."); + +/// Happens when syn can't interpret in `#[proptest ]`. +error!(no_interp_meta, E0024, + "The tokens `` in #[proptest ] do not make for a valid attribute."); + +/// Happens when both `#[proptest(strategy..)]` and `#[proptest(value..)]` +/// were specified. They are mutually exclusive choices. The user can resolve +/// this by picking one. +fatal!(overspecified_strat, E0025, + "Can not set `#[proptest(value = \"\")]` as well as \ + `#[proptest(params(strategy = \"\"))]` simultaneously. \ + Please pick one of those attributes."); + +/// Happens when `#[proptest(strategy..)]` or `#[proptest(value..)]` is +/// malformed. For example, `` inside `#[proptest(strategy = "")]` +/// or `#[proptest(value = "")]` is malformed. In other words, `` +/// is not a valid Rust expression. +error!(strategy_malformed(meta: &syn::Meta), E0026, + "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \ + format `#[proptest({0} = \"\")]` where `` is a valid Rust \ + expression.", + meta.name()); + +/// Happens when `#[proptest(filter..)]` is malformed. +/// For example, `` inside `#[proptest(filter = "")]` or +/// is malformed. In other words, `` is not a valid Rust expression. +error!(filter_malformed(meta: &syn::Meta), E0027, + "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \ + format `#[proptest({0} = \"\")]` where `` is a valid Rust \ + expression.", + meta.name()); + +/// Any attributes on a skipped variant has no effect - so we emit this error +/// to the user so that they are aware. +error!(skipped_variant_has_weight(item: &str), E0028, + "A variant has been skipped. Setting `#[proptest(weight = )]` on \ + the {} is meaningless and is not allowed.", + item); + +/// Any attributes on a skipped variant has no effect - so we emit this error +/// to the user so that they are aware. +error!(skipped_variant_has_param(item: &str), E0028, + "A variant has been skipped. Setting `#[proptest(no_param)]` or \ + `#[proptest(params())]` on the {} is meaningless and is not allowed.", + item); + +/// Any attributes on a skipped variant has no effect - so we emit this error +/// to the user so that they are aware. +error!(skipped_variant_has_strat(item: &str), E0028, + "A variant has been skipped. Setting `#[proptest(value = \"\")]` or \ + `#[proptest(strategy = \"\")]` on the {} is meaningless and is not \ + allowed.", + item); + +/// Any attributes on a skipped variant has no effect - so we emit this error +/// to the user so that they are aware. Unfortunately, there's no way to +/// emit a warning to the user, so we emit an error instead. +error!(skipped_variant_has_filter(item: &str), E0028, + "A variant has been skipped. Setting `#[proptest(filter = \"\")]` or \ + on the {} is meaningless and is not allowed.", + item); + +/// There's only one way to produce a specific unit variant, so setting +/// `#[proptest(strategy = "")]` or `#[proptest(value = "")]` +/// would be pointless. +error!(strategy_on_unit_variant(what: &str), E0029, + "Setting `#[proptest({0} = \"\")]` on a unit variant has no effect \ + and is redundant because there is nothing to configure.", + what); + +/// There's only one way to produce a specific unit variant, so setting +/// `#[proptest(params = "")]` would be pointless. +error!(params_on_unit_variant, E0029, + "Setting `#[proptest(params = \"\")]` on a unit variant has \ + no effect and is redundant because there is nothing to configure."); + +/// There's only one way to produce a specific unit variant, so setting +/// `#[proptest(filter = "")]` would be pointless. +error!(filter_on_unit_variant, E0029, + "Setting `#[proptest(filter = \"\")]` on a unit variant has \ + no effect and is redundant because there is nothing to further filter."); + +/// Occurs when `#[proptest(params = "")]` is specified on a unit +/// struct. There's only one way to produce a unit struct, so specifying +/// `Parameters` would be pointless. +error!(params_on_unit_struct, E0030, + "Setting `#[proptest(params = \"\")]` on a unit struct has no effect \ + and is redundant because there is nothing to configure."); + +/// Occurs when `#[proptest(filter = "")]` is specified on a unit +/// struct. There's only one way to produce a unit struct, so filtering +/// would be pointless. +error!(filter_on_unit_struct, E0030, + "Setting `#[proptest(filter = \"\")]` on a unit struct has no effect \ + and is redundant because there is nothing to filter."); + +/// Occurs when `#[proptest(no_bound)]` is specified +/// on something that is not a type variable. +error!(no_bound_set_on_non_tyvar, E0031, + "Setting `#[proptest(no_bound)]` on something that is not a type variable \ + has no effect and is redundant. Therefore it is not allowed."); + +/// Happens when `#[proptest(no_bound)]` is malformed. +error!(no_bound_malformed, E0032, + "The attribute modifier `no_bound` inside `#[proptest(..)]` does not \ + support any further configuration and must be a plain modifier as in \ + `#[proptest(no_bound)]`."); + +/// Happens when the sum of weights on enum variants overflowing an u32. +error!(weight_overflowing, E0033, + "The sum of the weights specified on variants of the enum you are \ + deriving `Arbitrary` for overflows an `u32` which it can't do."); diff --git a/proptest-derive/src/interp.rs b/proptest-derive/src/interp.rs new file mode 100644 index 00000000..8002f53c --- /dev/null +++ b/proptest-derive/src/interp.rs @@ -0,0 +1,281 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syn::{self, Expr as E, Lit as L, IntSuffix as IS, BinOp as B, UnOp as U}; + +/// Adapted from https://docs.rs/syn/0.14.2/src/syn/lit.rs.html#943 to accept +/// u128. +fn parse_lit_int(mut s: &str) -> Option { + /// Get the byte at offset idx, or a default of `b'\0'` if we're looking + /// past the end of the input buffer. + pub fn byte + ?Sized>(s: &S, idx: usize) -> u8 { + let s = s.as_ref(); + if idx < s.len() { + s[idx] + } else { + 0 + } + } + + let base = match (byte(s, 0), byte(s, 1)) { + (b'0', b'x') => { + s = &s[2..]; + 16 + } + (b'0', b'o') => { + s = &s[2..]; + 8 + } + (b'0', b'b') => { + s = &s[2..]; + 2 + } + (b'0'...b'9', _) => 10, + _ => unreachable!(), + }; + + let mut value = 0u128; + loop { + let b = byte(s, 0); + let digit = match b { + b'0'...b'9' => u128::from(b - b'0'), + b'a'...b'f' if base > 10 => 10 + u128::from(b - b'a'), + b'A'...b'F' if base > 10 => 10 + u128::from(b - b'A'), + b'_' => { + s = &s[1..]; + continue; + } + // NOTE: Looking at a floating point literal, we don't want to + // consider these integers. + b'.' if base == 10 => return None, + b'e' | b'E' if base == 10 => return None, + _ => break, + }; + + if digit >= base { + panic!("Unexpected digit {:x} out of base range", digit); + } + + value = value.checked_mul(base)?.checked_add(digit)?; + s = &s[1..]; + } + + Some(value) +} + +/// Parse a suffix of an integer literal. +fn parse_suffix(lit: &str) -> IS { + [("i8", IS::I8), + ("i16", IS::I16), + ("i32", IS::I32), + ("i64", IS::I64), + ("i128", IS::I128), + ("isize", IS::Isize), + ("u8", IS::U8), + ("u16", IS::U16), + ("u32", IS::U32), + ("u64", IS::U64), + ("u128", IS::U128), + ("usize", IS::Usize)] + .iter() + .find(|&(s, _)| lit.ends_with(s)) + .map(|(_, suffix)| suffix.clone()) + .unwrap_or(IS::None) +} + +/// Interprets an integer literal in a string. +fn eval_str_int(lit: &str) -> Option { + use std::{u8, u16, u32, u64, i8, i16, i32, i64, i128}; + use syn::IntSuffix::*; + + let val = parse_lit_int(lit)?; + Some(match parse_suffix(lit) { + None => val, + I8 if val <= i8::MAX as u128 => val, + I16 if val <= i16::MAX as u128 => val, + I32 if val <= i32::MAX as u128 => val, + I64 if val <= i64::MAX as u128 => val, + U8 if val <= u128::from(u8::MAX) => val, + U16 if val <= u128::from(u16::MAX) => val, + U32 if val <= u128::from(u32::MAX) => val, + U64 if val <= u128::from(u64::MAX) => val, + Usize if val <= u128::max_value() => val, + Isize if val <= i128::max_value() as u128 => val, + U128 => val, + I128 if val <= i128::MAX as u128 => val, + + // Does not fit in suffix: + _ => return Option::None, + }) +} + +/// Interprets an integer literal. +fn eval_lit_int(lit: &syn::LitInt) -> Option { + use quote::ToTokens; + eval_str_int(&lit.into_token_stream().to_string()) +} + +/// Interprets a verbatim literal. +fn eval_lit_verbatim(lit: &syn::LitVerbatim) -> Option { + let lit = lit.token.to_string(); + eval_str_int(&lit) +} + +/// Interprets a literal. +fn eval_lit(lit: &syn::ExprLit) -> Option { + match &lit.lit { + L::Int(lit) => eval_lit_int(lit), + L::Byte(lit) => Some(u128::from(lit.value())), + L::Verbatim(lit) => eval_lit_verbatim(lit), + _ => None + } +} + +/// Interprets a binary operator on two expressions. +fn eval_binary(bin: &syn::ExprBinary) -> Option { + use std::u32; + + let l = eval_expr(&bin.left)?; + let r = eval_expr(&bin.right)?; + Some(match bin.op { + B::Add(_) => l.checked_add(r)?, + B::Sub(_) => l.checked_sub(r)?, + B::Mul(_) => l.checked_mul(r)?, + B::Div(_) => l.checked_div(r)?, + B::Rem(_) => l.checked_rem(r)?, + B::BitXor(_) => l ^ r, + B::BitAnd(_) => l & r, + B::BitOr(_) => l | r, + B::Shl(_) if r <= u128::from(u32::MAX) + => l.checked_shl(r as u32)?, + B::Shr(_) if r <= u128::from(u32::MAX) + => l.checked_shr(r as u32)?, + _ => return None, + }) +} + +/// Interprets unary operator on an expression. +fn eval_unary(expr: &syn::ExprUnary) -> Option { + if let U::Not(_) = expr.op { + Some(!eval_expr(&expr.expr)?) + } else { + None + } +} + +/// A **very** simple CTFE interpreter for some basic arithmetic: +pub fn eval_expr(expr: &E) -> Option { + match expr { + E::Lit(expr) => eval_lit(expr), + E::Binary(expr) => eval_binary(expr), + E::Unary(expr) => eval_unary(expr), + E::Paren(expr) => eval_expr(&expr.expr), + E::Group(expr) => eval_expr(&expr.expr), + _ => None, + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn eval(expr: &str) -> Option { + use syn::parse_str; + eval_expr(&parse_str(expr).expect("not a valid expression")) + } + + macro_rules! test { + ($($name: ident, $case: expr => $result:expr;)*) => {$( + #[test] fn $name() { assert_eq!(eval($case), $result); } + )*}; + } + + test! { + accept_lit_bare, "1" => Some(1); + accept_lit_bare_max, "340282366920938463463374607431768211455" + => Some(340282366920938463463374607431768211455); + reject_lit_bare_overflow, "340282366920938463463374607431768211456" => None; + accept_lit_u8_max, "255u8" => Some(255); + accept_lit_u16_max, "65535u16" => Some(65535); + accept_lit_u32_max, "4294967295u32" => Some(4294967295); + accept_lit_u64_max, "18446744073709551615u64" => Some(18446744073709551615); + accept_lit_u128_max, "340282366920938463463374607431768211455u128" + => Some(340282366920938463463374607431768211455); + reject_lit_u8_overflow, "256u8" => None; + reject_lit_u16_overflow, "65536u16" => None; + reject_lit_u32_overflow, "4294967296u32" => None; + reject_lit_u64_overflow, "18446744073709551616u64" => None; + reject_lit_u128_overflow, "340282366920938463463374607431768211456u128" => None; + accept_lit_i8_max, "127i8" => Some(127); + accept_lit_i16_max, "32767i16" => Some(32767); + accept_lit_i32_max, "2147483647i32" => Some(2147483647); + accept_lit_i64_max, "9223372036854775807i64" => Some(9223372036854775807); + accept_lit_i128_max, "170141183460469231731687303715884105727i128" + => Some(170141183460469231731687303715884105727); + reject_lit_i8_overflow, "128i8" => None; + reject_lit_i16_overflow, "32768i16" => None; + reject_lit_i32_overflow, "2147483648i32" => None; + reject_lit_i64_overflow, "9223372036854775808i64" => None; + reject_lit_i128_overflow, "170141183460469231731687303715884105728i128" => None; + accept_lit_usize, "42usize" => Some(42); + accept_lit_isize, "42isize" => Some(42); + accept_lit_byte, "b'0'" => Some(48); + reject_lit_negative, "-42" => None; + accept_add_10_20, "10 + 20" => Some(30); + accept_add_10u8_20u16, "10u8 + 20u16" => Some(30); + reject_add_overflow, "340282366920938463463374607431768211456u128 + 1" => None; + accept_add_commutes, "20 + 10" => Some(30); + accept_add_5_numbers, "(10 + 20) + 30 + (40 + 50)" => Some(150); + accept_add_10_0, "10 + 0" => Some(10); + accept_sub_20_10, "20 - 10" => Some(10); + reject_sub_10_20, "10 - 20" => None; + reject_sub_10_11, "10 - 11" => None; + accept_sub_10_10, "10 - 10" => Some(0); + accept_mul_42_0, "42 * 0" => Some(0); + accept_mul_0_42, "0 * 42" => Some(0); + accept_mul_42_1, "42 * 1" => Some(42); + accept_mul_1_42, "1 * 42" => Some(42); + accept_mul_3_4, "3 * 4" => Some(12); + accept_mul_4_3, "4 * 3" => Some(12); + accept_mul_1_2_3_4_5, "(1 * 2) * 3 * (4 * 5)" => Some(120); + reject_div_with_0, "10 / 0" => None; + accept_div_42_1, "42 / 1" => Some(42); + accept_div_42_42, "42 / 42" => Some(1); + accept_div_20_10, "20 / 10" => Some(2); + accept_div_10_20, "10 / 20" => Some(0); + reject_rem_with_0, "10 % 0" => None; + accept_rem_0_4, "0 % 4" => Some(0); + accept_rem_4_4, "4 % 4" => Some(0); + accept_rem_8_4, "8 % 4" => Some(0); + accept_rem_1_4, "1 % 4" => Some(1); + accept_rem_5_4, "5 % 4" => Some(1); + accept_rem_2_4, "2 % 4" => Some(2); + accept_rem_3_4, "3 % 4" => Some(3); + accept_xor_1, "0b0000 ^ 0b1111" => Some(0b1111); + accept_xor_2, "0b1111 ^ 0b0000" => Some(0b1111); + accept_xor_3, "0b1111 ^ 0b1111" => Some(0b0000); + accept_xor_4, "0b0000 ^ 0b0000" => Some(0b0000); + accept_xor_5, "0b1100 ^ 0b0011" => Some(0b1111); + accept_xor_6, "0b1001 ^ 0b1111" => Some(0b0110); + accept_and_1, "0b0000 & 0b0000" => Some(0b0000); + accept_and_2, "0b1001 & 0b0101" => Some(0b0001); + accept_and_3, "0b1111 & 0b1111" => Some(0b1111); + accept_or_1, "0b0000 | 0b0000" => Some(0b0000); + accept_or_2, "0b1001 | 0b0101" => Some(0b1101); + accept_or_3, "0b1111 | 0b1111" => Some(0b1111); + accept_shl, "0b001000 << 2" => Some(0b100000); + accept_shr, "0b001000 >> 2" => Some(0b000010); + accept_shl_zero, "0b001000 << 0" => Some(0b001000); + accept_shr_zero, "0b001000 >> 0" => Some(0b001000); + reject_shl_rhs_not_u32, "0b001000 << 4294967296" => None; + reject_shl_overflow, "0b001000 << 429496" => None; + reject_shr_rhs_not_u32, "0b001000 >> 4294967296" => None; + reject_shr_underflow, "0b001000 >> 429496" => None; + accept_complex_arith, "(3 + 4 * 2 - 5) / 6" => Some(1); + } +} diff --git a/proptest-derive/src/lib.rs b/proptest-derive/src/lib.rs new file mode 100644 index 00000000..5dffec5e --- /dev/null +++ b/proptest-derive/src/lib.rs @@ -0,0 +1,55 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! TODO + +// # Known issues +// +// ## Fields with `[T; N]` where `N > 32` +// +// We can't derive for fields having arrays with sizes over 32. +// While proptest only supports in UniformArrayStrategy arrays of sizes up to +// 32, we can overcome that restriction by generating custom types on the +// fly here. What we can't overcome is that `T: Arbititrary |- T: Debug` due +// to the requirement by proptest. Since `T: Debug` must hold, we must also +// ensure that arrays with sizes over 33 are also Debug. We can't do this. +// Doing so would create orphan instances, which Rust does not allow to preserve +// coherence. Therefore, until const generics lands in stable or when +// we can remove the `T: Debug` bound on Arbitrary, we can not support arrays +// sized over 32. +// +// # Recursive types +// +// We can't handle self-recursive or mutually recursive types at all right now. + +extern crate proc_macro as pm; +extern crate proc_macro2; + +#[macro_use] +extern crate syn; +#[macro_use] +extern crate quote; + +mod util; +mod interp; +mod void; +mod error; +mod use_tracking; +mod attr; +mod ast; +mod derive; + +/// See module level documentation for more information. +#[proc_macro_derive(Arbitrary, attributes(proptest))] +pub fn derive_proptest_arbitrary(input: pm::TokenStream) -> pm::TokenStream { + // Bootstrap! + // This function just delegates to impl_proptest_arbitrary. + derive::impl_proptest_arbitrary(syn::parse(input).unwrap()).into() +} + +#[cfg(test)] mod tests; diff --git a/proptest-derive/src/tests.rs b/proptest-derive/src/tests.rs new file mode 100644 index 00000000..339c3ddb --- /dev/null +++ b/proptest-derive/src/tests.rs @@ -0,0 +1,136 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module provides integration tests that test the expansion +//! of the derive macro. + +extern crate proc_macro2; + +//============================================================================== +// Macros: +//============================================================================== + +// Borrowed from: +// https://docs.rs/synstructure/0.7.0/src/synstructure/macros.rs.html#104-135 +macro_rules! test_derive { + ($name:path { $($i:tt)* } expands to { $($o:tt)* }) => { + { + #[allow(dead_code)] + fn ensure_compiles() { + $($i)* + $($o)* + } + + test_derive!($name { $($i)* } expands to { $($o)* } no_build); + } + }; + ($name:path { $($i:tt)* } expands to { $($o:tt)* } no_build) => { + { + let expected = stringify!( $($o)* ) + .parse::<$crate::tests::proc_macro2::TokenStream>() + .expect("output should be a valid TokenStream"); + + let i = stringify!( $($i)* ); + let parsed = $crate::syn::parse_str::<$crate::syn::DeriveInput>(i).expect( + concat!("Failed to parse input to `#[derive(", + stringify!($name), + ")]`") + ); + let res = $name(parsed); + assert_eq!( + format!("{}", res), + format!("{}", expected) + ); + } + }; +} + +macro_rules! test { + (no_build $test_name:ident { $($i:tt)* } expands to { $($o:tt)* }) => { + #[test] + fn $test_name() { + test_derive!( + $crate::derive::impl_proptest_arbitrary { $($i)* } + expands to { $($o)* } no_build + ); + } + }; + ($test_name:ident { $($i:tt)* } expands to { $($o:tt)* }) => { + #[test] + fn $test_name() { + test_derive!( + $crate::derive::impl_proptest_arbitrary { $($i)* } + expands to { $($o)* } + ); + } + }; +} + +//============================================================================== +// Unit structs: +//============================================================================== + +test! { + struct_unit_unit { + #[derive(Debug)] + struct MyUnitStruct; + } expands to { + #[allow(non_upper_case_globals)] + const _IMPL_ARBITRARY_FOR_MyUnitStruct : () = { + extern crate proptest as _proptest; + impl _proptest::arbitrary::Arbitrary for MyUnitStruct { + type Parameters = (); + type Strategy = fn() -> Self; + + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + || MyUnitStruct {} + } + } + }; + } +} + +test! { + struct_unit_tuple { + #[derive(Debug)] + struct MyTupleUnitStruct(); + } expands to { + #[allow(non_upper_case_globals)] + const _IMPL_ARBITRARY_FOR_MyTupleUnitStruct : () = { + extern crate proptest as _proptest; + impl _proptest::arbitrary::Arbitrary for MyTupleUnitStruct { + type Parameters = (); + type Strategy = fn() -> Self; + + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + || MyTupleUnitStruct {} + } + } + }; + } +} + +test! { + struct_unit_named { + #[derive(Debug)] + struct MyNamedUnitStruct {} + } expands to { + #[allow(non_upper_case_globals)] + const _IMPL_ARBITRARY_FOR_MyNamedUnitStruct : () = { + extern crate proptest as _proptest; + impl _proptest::arbitrary::Arbitrary for MyNamedUnitStruct { + type Parameters = (); + type Strategy = fn() -> Self; + + fn arbitrary_with(_top: Self::Parameters) -> Self::Strategy { + || MyNamedUnitStruct {} + } + } + }; + } +} diff --git a/proptest-derive/src/use_tracking.rs b/proptest-derive/src/use_tracking.rs new file mode 100644 index 00000000..767c499a --- /dev/null +++ b/proptest-derive/src/use_tracking.rs @@ -0,0 +1,231 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Provides `UseTracker` as well as `UseMarkable` which is used to +//! track uses of type variables that need `Arbitrary` bounds in our +//! impls. + +// Perhaps ordermap would be better, but our maps are so small that we care +// much more about the increased compile times incured by including ordermap. +// We need to preserve insertion order in any case, so HashMap is not useful. +use std::collections::{BTreeMap, HashSet}; +use std::borrow::Borrow; + +use syn; + +use attr; +use util; +use error::{DeriveResult, Ctx}; + +//============================================================================== +// API: Type variable use tracking +//============================================================================== + +/// `UseTracker` tracks what type variables that have used in `any_with::` +/// or similar and thus needs an `Arbitrary` bound added to them. +pub struct UseTracker { + /// Tracks 'usage' of a type variable name. + /// Allocation of this map will happen at once and no further + /// allocation will happen after that. Only potential updates + /// will happen after initial allocation. + used_map: BTreeMap, + /// Extra types to bound by `Arbitrary` in the `where` clause. + where_types: HashSet, + /// The generics that we are doing this for. + /// This what we will modify later once we're done. + generics: syn::Generics, + /// If set to `true`, then `mark_used` has no effect. + track: bool, +} + +/// Models a thing that may have type variables in it that +/// can be marked as 'used' as defined by `UseTracker`. +pub trait UseMarkable { + fn mark_uses(&self, tracker: &mut UseTracker); +} + +impl UseTracker { + /// Constructs the tracker for the given `generics`. + pub fn new(generics: syn::Generics) -> Self { + // Construct the map by setting all type variables as being unused + // initially. This is the only time we will allocate for the map. + let used_map = generics.type_params() + .map(|v| (v.ident.clone(), false)) + .collect(); + Self { + generics, + used_map, + where_types: HashSet::default(), + track: true + } + } + + /// Stop tracking. `.mark_used` will have no effect. + pub fn no_track(&mut self) { + self.track = false; + } + + /// Mark the _potential_ type variable `tyvar` as used. + /// If the tracker does not know about the name, it is not + /// a type variable and this call has no effect. + fn use_tyvar(&mut self, tyvar: impl Borrow) { + if self.track { + if let Some(used) = self.used_map.get_mut(tyvar.borrow()) { + *used = true; + } + } + } + + /// Returns true iff the type variable given exists. + fn has_tyvar(&self, ty_var: impl Borrow) -> bool { + self.used_map.contains_key(ty_var.borrow()) + } + + /// Mark the type as used. + fn use_type(&mut self, ty: syn::Type) { + self.where_types.insert(ty); + } + + /// Adds the bound in `for_used` on used type variables and + /// the bound in `for_not` (`if .is_some()`) on unused type variables. + pub fn add_bounds(&mut self, ctx: Ctx, + for_used: &syn::TypeParamBound, for_not: Option) + -> DeriveResult<()> + { + { + let mut iter = self.used_map.values().zip(self.generics.type_params_mut()); + if let Some(for_not) = for_not { + iter.try_for_each(|(&used, tv)| { + // Steal the attributes: + let no_bound = attr::has_no_bound(ctx, &tv.attrs)?; + let bound = if used && !no_bound { for_used } else { &for_not }; + tv.bounds.push(bound.clone()); + Ok(()) + })?; + } else { + iter.for_each(|(&used, tv)| + if used { tv.bounds.push(for_used.clone()) } + ) + } + } + + self.generics.make_where_clause().predicates.extend( + self.where_types.iter().cloned().map(|ty| + syn::WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty: ty, + colon_token: ::default(), + bounds: ::std::iter::once(for_used.clone()).collect(), + }) + ) + ); + + Ok(()) + } + + /// Consumes the (potentially) modified generics that the + /// tracker was originally constructed with and returns it. + pub fn consume(self) -> syn::Generics { + self.generics + } +} + +//============================================================================== +// Impls +//============================================================================== + +impl UseMarkable for syn::Type { + fn mark_uses(&self, ut: &mut UseTracker) { + use syn::visit; + + visit::visit_type(&mut PathVisitor(ut), self); + + struct PathVisitor<'ut>(&'ut mut UseTracker); + + impl<'ut, 'ast> visit::Visit<'ast> for PathVisitor<'ut> { + fn visit_macro(&mut self, _: &syn::Macro) {} + + fn visit_type_path(&mut self, tpath: &syn::TypePath) { + if matches_prj_tyvar(self.0, tpath) { + self.0.use_type(adjust_simple_prj(tpath).into()); + return; + } + visit::visit_type_path(self, tpath); + } + + fn visit_path(&mut self, path: &syn::Path) { + // If path is PhantomData do not mark innards. + if util::is_phantom_data(path) { return; } + + if let Some(ident) = util::extract_simple_path(path) { + self.0.use_tyvar(ident); + } + + visit::visit_path(self, path); + } + } + } +} + +fn matches_prj_tyvar(ut: &mut UseTracker, tpath: &syn::TypePath) -> bool { + let path = &tpath.path; + let segs = &path.segments; + + if let Some(qself) = &tpath.qself { + // < $qself > :: $path + if let Some(sub_tp) = extract_path(&qself.ty) { + return sub_tp.qself.is_none() + && util::match_singleton(segs.iter().skip(qself.position)) + .filter(|ps| ps.arguments.is_empty()) + .and_then(|_| util::extract_simple_path(&sub_tp.path)) + .filter(|&ident| ut.has_tyvar(ident)) + .is_some() // < $tyvar as? $path? > :: $path + || matches_prj_tyvar(ut, sub_tp); + } + + false + } else { + // true => $tyvar :: $projection + return !path.global() && segs.len() == 2 + && ut.has_tyvar(&segs[0].ident) + && segs[0].arguments.is_empty() + && segs[1].arguments.is_empty(); + } +} + +fn adjust_simple_prj(tpath: &syn::TypePath) -> syn::TypePath { + let segments = tpath.qself.as_ref() + .filter(|qp| qp.as_token.is_none()) + .and_then(|qp| extract_path(&*qp.ty)) + .filter(|tp| tp.qself.is_none()) + .map(|tp| &tp.path.segments); + + if let Some(segments) = segments { + let tpath = tpath.clone(); + let mut segments = segments.clone(); + segments.push_punct(::default()); + segments.extend(tpath.path.segments.into_pairs()); + syn::TypePath { + qself: None, + path: syn::Path { + leading_colon: None, + segments + } + } + } else { + tpath.clone() + } +} + +fn extract_path(ty: &syn::Type) -> Option<&syn::TypePath> { + if let syn::Type::Path(tpath) = ty { + Some(tpath) + } else { + None + } +} diff --git a/proptest-derive/src/util.rs b/proptest-derive/src/util.rs new file mode 100644 index 00000000..92e35fcf --- /dev/null +++ b/proptest-derive/src/util.rs @@ -0,0 +1,129 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Mostly useful utilities for syn used in the crate. + +use std::borrow::Borrow; + +use syn; + +//============================================================================== +// General AST manipulation and types +//============================================================================== + +/// Extract the list of fields from a `Fields` from syn. +/// We don't care about the style, we always and uniformly use {} in +/// struct literal syntax for making struct and enum variant values. +pub fn fields_to_vec(fields: syn::Fields) -> Vec { + use syn::Fields::*; + match fields { + Named(fields) => fields.named.into_iter().collect(), + Unnamed(fields) => fields.unnamed.into_iter().collect(), + Unit => vec![] + } +} + +/// Returns true iff the given type is the literal unit type `()`. +/// This is treated the same way by `syn` as a 0-tuple. +pub fn is_unit_type>(ty: T) -> bool { + ty.borrow() == &parse_quote!(()) +} + +/// Returns the `Self` type (in the literal syntactic sense). +pub fn self_ty() -> syn::Type { + parse_quote!(Self) +} + +//============================================================================== +// Paths: +//============================================================================== + +type CommaPS = syn::punctuated::Punctuated; + +/// Returns true iff the path is simple, i.e: +/// just a :: separated list of identifiers. +fn is_path_simple(path: &syn::Path) -> bool { + path.segments.iter().all(|ps| ps.arguments.is_empty()) +} + +/// Returns true iff lhs matches the rhs. +fn eq_simple_pathseg(lhs: &str, rhs: &CommaPS) -> bool { + lhs.split("::").filter(|s| !s.trim().is_empty()) + .eq(rhs.iter().map(|ps| ps.ident.to_string())) +} + +/// Returns true iff lhs matches the given simple Path. +pub fn eq_simple_path(mut lhs: &str, rhs: &syn::Path) -> bool { + if !is_path_simple(rhs) { return false } + + if rhs.leading_colon.is_some() { + if !lhs.starts_with("::") { return false } + lhs = &lhs[2..]; + } + + eq_simple_pathseg(lhs, &rhs.segments) +} + +/// Returns true iff the given path matches any of given +/// paths specified as string slices. +pub fn match_pathsegs(path: &syn::Path, against: &[&str]) -> bool { + against.iter().any(|needle| eq_simple_path(needle, path)) +} + +/// Returns true iff the given `PathArguments` is one that has one type +/// applied to it. +fn pseg_has_single_tyvar(pp: &syn::PathSegment) -> bool { + use syn::GenericArgument::Type; + use syn::PathArguments::AngleBracketed; + if let AngleBracketed(ab) = &pp.arguments { + if let Some(Type(_)) = match_singleton(ab.args.iter()) { + return true; + } + } + false +} + +/// Returns true iff the given type is of the form `PhantomData` where +/// `TY` can be substituted for any type, including type variables. +pub fn is_phantom_data(path: &syn::Path) -> bool { + let segs = &path.segments; + if segs.is_empty() { return false } + + let mut path = path.clone(); + let lseg = path.segments.pop().unwrap().into_value(); + + &lseg.ident == "PhantomData" && + pseg_has_single_tyvar(&lseg) && + match_pathsegs(&path, &[ + // We hedge a bet that user will never declare + // their own type named PhantomData. + // This may give errors, but is worth it usability-wise. + "", + "marker", + "std::marker", + "core::marker", + "::std::marker", + "::core::marker", + ]) +} + +/// Extracts a simple non-global path of length 1. +pub fn extract_simple_path(path: &syn::Path) -> Option<&syn::Ident> { + match_singleton(&path.segments) + .filter(|f| !path.global() && f.arguments.is_empty()) + .map(|f| &f.ident) +} +//============================================================================== +// General Rust utilities: +//============================================================================== + +/// Returns `Some(x)` iff the iterable is singleton and otherwise None. +pub fn match_singleton(it: impl IntoIterator) -> Option { + let mut it = it.into_iter(); + it.next().filter(|_| it.next().is_none()) +} diff --git a/proptest-derive/src/void.rs b/proptest-derive/src/void.rs new file mode 100644 index 00000000..c9bb6423 --- /dev/null +++ b/proptest-derive/src/void.rs @@ -0,0 +1,162 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Provides the `IsUninhabited` trait. +//! +//! By nature, determining if a type is uninhabited or not given Rust's +//! turing complete type system is undecidable. Furthermore, we don't even +//! have access to all the information because we can't inspect type +//! definitions, type macros, or projections via associated types. +//! +//! Any analysis we perform here is therefore incomplete but sound. +//! That is, if we state that a type is uninhabited, it is so for sure. +//! But we can't state thta all uninhabited types are uninhabited. + +use syn::{self, visit}; + +use util; +use interp; + +//============================================================================== +// Trait +//============================================================================== + +/// A trait for types for which it is possible to check if the modelled +/// object is uninhabited or not. A `false` answer means that we can not +/// tell for sure that the thing is uninhabited, not that we are 100% +/// certain that it is inhabited. +pub trait IsUninhabited { + /// Returns true if the given type is known to be uninhabited. + /// There may be more scenarios under which the type is uninhabited. + /// Thus, this is not a complete and exhaustive check. + fn is_uninhabited(&self) -> bool; +} + +//============================================================================== +// Enum/Variants: +//============================================================================== + +impl IsUninhabited for syn::DataEnum { + fn is_uninhabited(&self) -> bool { + self.variants.is_uninhabited() + } +} + +impl

IsUninhabited for syn::punctuated::Punctuated { + fn is_uninhabited(&self) -> bool { + self.iter().all(IsUninhabited::is_uninhabited) + } +} + +impl<'a> IsUninhabited for &'a [syn::Variant] { + fn is_uninhabited(&self) -> bool { + self.iter().all(IsUninhabited::is_uninhabited) + } +} + +impl IsUninhabited for syn::Variant { + fn is_uninhabited(&self) -> bool { + self.fields.is_uninhabited() + } +} + +//============================================================================== +// Struct/Fields: +//============================================================================== + +impl IsUninhabited for syn::Fields { + fn is_uninhabited(&self) -> bool { + self.iter().any(syn::Field::is_uninhabited) + } +} + +impl<'a> IsUninhabited for &'a [syn::Field] { + fn is_uninhabited(&self) -> bool { + self.iter().any(syn::Field::is_uninhabited) + } +} + +impl IsUninhabited for syn::Field { + fn is_uninhabited(&self) -> bool { + self.ty.is_uninhabited() + } +} + +//============================================================================== +// Types: +//============================================================================== + +impl IsUninhabited for syn::Type { + fn is_uninhabited(&self) -> bool { + let mut uninhabited = Uninhabited(false); + visit::visit_type(&mut uninhabited, &self); + uninhabited.0 + } +} + +/// Tracks uninhabitedness. +struct Uninhabited(bool); + +impl Uninhabited { + /// Set to uninhabited. + fn set(&mut self) { self.0 = true; } +} + +// We are more strict than Rust is. +// Our notion of uninhabited is if the type is generatable or not. +// The second a type like *const ! is dereferenced you have UB. + +impl<'ast> visit::Visit<'ast> for Uninhabited { + //------------------------------------------------------------------ + // If we get to one of these we have a knowably uninhabited type: + //------------------------------------------------------------------ + + // The ! (never) type is obviously uninhabited: + fn visit_type_never(&mut self, _: &'ast syn::TypeNever) { + self.set(); + } + + // A path is uninhabited if we get one we know is uninhabited. + // Even if `T` in `::Item` is uninhabited, the associated item + // may be inhabited, so we can't say for sure that it is uninhabited. + fn visit_type_path(&mut self, type_path: &'ast syn::TypePath) { + const KNOWN_UNINHABITED: &[&str] = &[ + "std::string::ParseError", + "::std::string::ParseError", + ]; + + if type_path.qself.is_none() && + util::match_pathsegs(&type_path.path, KNOWN_UNINHABITED) { + self.set(); + } + } + + // An array is uninhabited iff: `[T; N]` where uninhabited(T) && N != 0 + // We want to block decent if N == 0. + fn visit_type_array(&mut self, arr: &'ast syn::TypeArray) { + if let Some(len) = interp::eval_expr(&arr.len) { + if len > 0 { + self.visit_type(&arr.elem); + } + } + } + + //------------------------------------------------------------------ + // These are here to block decent: + //------------------------------------------------------------------ + + // An fn(I) -> O is never uninhabited even if I or O are: + fn visit_type_bare_fn(&mut self, _: &'ast syn::TypeBareFn) {} + + // A macro may transform the inner type in ways we can't predict: + fn visit_macro(&mut self, _: &'ast syn::Macro) {} + + // Both of these could be, but type is anonymous: + fn visit_type_impl_trait(&mut self, _: &'ast syn::TypeImplTrait) {} + fn visit_type_trait_object(&mut self, _: &'ast syn::TypeTraitObject) {} +} diff --git a/proptest-derive/tests/assoc.rs b/proptest-derive/tests/assoc.rs new file mode 100755 index 00000000..7b54feb5 --- /dev/null +++ b/proptest-derive/tests/assoc.rs @@ -0,0 +1,243 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::Arbitrary; + +trait Func { type Out; } +trait FuncA { type OutA: FuncB; } +trait FuncB { type OutB; } + +#[derive(Debug)] +struct TypeA; + +#[derive(Debug)] +struct TypeB; + +#[derive(Debug, Arbitrary)] +struct OutTy { + #[proptest(value = "42")] + val: usize, +} + +impl Func for TypeA { type Out = OutTy; } +impl FuncA for TypeA { type OutA = TypeB; } +impl FuncB for TypeB { type OutB = OutTy; } + +#[derive(Debug, Arbitrary)] +struct T0 { + field: ::Out, +} + +#[derive(Debug, Arbitrary)] +struct T1 { + field: Vec, +} + +#[derive(Debug, Arbitrary)] +struct T2 { + field: Vec>, +} + +#[derive(Debug, Arbitrary)] +struct T3 { + field: Vec<::Out>, +} + +#[derive(Debug, Arbitrary)] +struct T4 { + field: Tyvar::OutB, +} + +#[derive(Arbitrary)] +struct T5 { + field: ::OutB, +} + +#[derive(Arbitrary)] +struct T6 { + field: ::OutB, +} + +#[derive(Arbitrary)] +struct T7 { + field: ::OutB, +} + +#[derive(Arbitrary)] +struct T8 { + field: <::OutA as FuncB>::OutB, +} + +#[derive(Arbitrary)] +struct T9 { + field: <::OutA as FuncB>::OutB, +} + +#[derive(Debug, Arbitrary)] +struct T10 { + field: Vec, +} + +#[derive(Arbitrary)] +struct T11 { + field: Vec<::OutB>, +} + +#[derive(Arbitrary)] +struct T12 { + field: Vec<::OutB>, +} + +#[derive(Arbitrary)] +struct T13 { + field: Vec<::OutB>, +} + +#[derive(Arbitrary)] +struct T14 { + field: Vec<<::OutA as FuncB>::OutB>, +} + +#[derive(Arbitrary)] +struct T15 { + field: Vec<<::OutA as FuncB>::OutB>, +} + +macro_rules! debug { + ($trait: path, $ty: ident) => { + impl ::std::fmt::Debug for $ty { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) + -> Result<(), ::std::fmt::Error> + { + fmt.debug_struct(stringify!($ty)) + .field("field", &"") + .finish() + } + } + } +} + +debug!(FuncB, T5); +debug!(FuncB, T6); +debug!(FuncA, T7); +debug!(FuncA, T8); +debug!(FuncA, T9); + +debug!(FuncB, T11); +debug!(FuncB, T12); +debug!(FuncA, T13); +debug!(FuncA, T14); +debug!(FuncA, T15); + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); + assert_arbitrary::>(); +} + +proptest! { + #[test] + fn t0_field_val_42(t: T0) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t1_no_panic(_: T1) {} + + #[test] + fn t2_no_panic(_: T2) {} + + #[test] + fn t3_all_42(t: T3) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } + + #[test] + fn t4_field_val_42(t: T4) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t5_field_val_42(t: T5) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t6_field_val_42(t: T6) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t7_field_val_42(t: T7) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t8_field_val_42(t: T8) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t9_field_val_42(t: T9) { + prop_assert_eq!(t.field.val, 42); + } + + #[test] + fn t10_all_42(t: T10) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } + + #[test] + fn t11_all_42(t: T11) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } + + #[test] + fn t12_all_42(t: T12) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } + + #[test] + fn t13_all_42(t: T13) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } + + #[test] + fn t14_all_42(t: T14) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } + + #[test] + fn t15_all_42(t: T15) { + t.field.iter().for_each(|x| assert_eq!(x.val, 42)) + } +} \ No newline at end of file diff --git a/proptest-derive/tests/compile-fail/E0001-lifetime.rs b/proptest-derive/tests/compile-fail/E0001-lifetime.rs new file mode 100644 index 00000000..a618c227 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0001-lifetime.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0001] + //~| [proptest_derive, E0008] +#[proptest(skip)] +struct NonFatal<'a>(&'a ()); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0001] +struct T0<'a>(&'a ()); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0001] +enum T1<'a> { + V0(&'a ()) +} diff --git a/proptest-derive/tests/compile-fail/E0002-no-unions.rs b/proptest-derive/tests/compile-fail/E0002-no-unions.rs new file mode 100644 index 00000000..4db9986d --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0002-no-unions.rs @@ -0,0 +1,13 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Arbitrary)] //~ ERROR: [proptest_derive, E0002] +union Foo { x: usize } \ No newline at end of file diff --git a/proptest-derive/tests/compile-fail/E0003-void-struct.rs b/proptest-derive/tests/compile-fail/E0003-void-struct.rs new file mode 100644 index 00000000..506910c4 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0003-void-struct.rs @@ -0,0 +1,45 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0003] + //~| [proptest_derive, E0008] +struct NonFatal { + #[proptest(skip)] + x: !, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0003] +struct Ty0 { x: ! } + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0003] +struct Ty1 { + x: usize, + y: !, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0003] +struct Ty2 { + x: (!, usize), + y: bool, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0003] +struct Ty3 { + x: [!; 1] +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0003] +struct Ty4 { + x: [::std::string::ParseError; 1], +} diff --git a/proptest-derive/tests/compile-fail/E0004-void-enum.rs b/proptest-derive/tests/compile-fail/E0004-void-enum.rs new file mode 100644 index 00000000..dc576fd8 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0004-void-enum.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0004] +enum Void {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0004] +enum FooBar {} diff --git a/proptest-derive/tests/compile-fail/E0005-void-enum.rs b/proptest-derive/tests/compile-fail/E0005-void-enum.rs new file mode 100644 index 00000000..afdad3a2 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0005-void-enum.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0005] +enum T0 { + V0(!), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0005] +enum T1 { + V0(!, bool), + V1([!; 1]), + V2([(!, bool); 1]) +} diff --git a/proptest-derive/tests/compile-fail/E0006-void-enum.rs b/proptest-derive/tests/compile-fail/E0006-void-enum.rs new file mode 100644 index 00000000..25f9ad9f --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0006-void-enum.rs @@ -0,0 +1,52 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0006] + //~| [proptest_derive, E0008] +enum NonFatal<#[proptest(skip)] T> { + #[proptest(skip)] + Unit(T), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0006] +enum T0 { + #[proptest(skip)] + Unit, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0006] +enum T1 { + #[proptest(skip)] + V0, + #[proptest(skip)] + V1, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0006] +enum T2 { + #[proptest(skip)] + V0, + #[proptest(skip)] + V1, + V2(!), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0006] +enum T3 { + #[proptest(skip)] + V0, + #[proptest(skip)] + V1, + V2([!; 1 + 2 + (3 / 3) + (1 << 3)]), +} diff --git a/proptest-derive/tests/compile-fail/E0007-illegal-strategy.rs b/proptest-derive/tests/compile-fail/E0007-illegal-strategy.rs new file mode 100644 index 00000000..d986e5e8 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0007-illegal-strategy.rs @@ -0,0 +1,37 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0007] + //~| [proptest_derive, E0030] +#[proptest(params = "u8")] +#[proptest(strategy = "1u8..")] +struct A {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0007] +#[proptest(strategy = "1u8..")] +struct B; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0007] +#[proptest(strategy = "1u8..")] +struct C(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0007] +#[proptest(strategy = "1u8..")] +struct D { field: String } + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0007] +#[proptest(strategy = "1u8..")] +struct E(Vec); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0007] +#[proptest(strategy = "1u8..")] +enum F { V1, V2, } diff --git a/proptest-derive/tests/compile-fail/E0008-illegal-skip.rs b/proptest-derive/tests/compile-fail/E0008-illegal-skip.rs new file mode 100644 index 00000000..074d6e64 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0008-illegal-skip.rs @@ -0,0 +1,62 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Clone, Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0008] + //~| [proptest_derive, E0007] +#[proptest(skip)] +#[proptest(strategy = "Just(A {})")] +struct A {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +#[proptest(skip)] +struct B; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +#[proptest(skip)] +struct C(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +#[proptest(skip)] +struct D { field: String } + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +#[proptest(skip)] +struct E(Vec); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +#[proptest(skip)] +enum F { V1, V2, } + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +struct G( + #[proptest(skip)] + Vec +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +struct H { + #[proptest(skip)] + field: Vec +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +enum I { + V0 { + #[proptest(skip)] + field: Vec + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0008] +enum J { + V0(#[proptest(skip)] Vec) +} diff --git a/proptest-derive/tests/compile-fail/E0009-illegal-weight.rs b/proptest-derive/tests/compile-fail/E0009-illegal-weight.rs new file mode 100644 index 00000000..f4d3a9bc --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0009-illegal-weight.rs @@ -0,0 +1,62 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0009] + //~| [proptest_derive, E0030] +#[proptest(no_params)] +#[proptest(weight = 1)] +struct A {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +#[proptest(weight = 2)] +struct B; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +#[proptest(weight = 3)] +struct C(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +#[proptest(weight = 3)] +struct D { field: String } + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +#[proptest(weight = 3)] +struct E(Vec); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +#[proptest(weight = 3)] +enum F { V1, V2, } + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +struct G( + #[proptest(weight = 3)] + Vec +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +struct H { + #[proptest(weight = 3)] + field: Vec +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +enum I { + V0 { + #[proptest(weight = 3)] + field: Vec + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0009] +enum J { + V0(#[proptest(weight = 3)] Vec) +} diff --git a/proptest-derive/tests/compile-fail/E0010-parent-has-params.rs b/proptest-derive/tests/compile-fail/E0010-parent-has-params.rs new file mode 100644 index 00000000..baa5486e --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0010-parent-has-params.rs @@ -0,0 +1,115 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0010] + //~| [proptest_derive, E0008] +#[proptest(no_params)] +struct NonFatal<#[proptest(skip)] T> { + #[proptest(no_params)] + field: T +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +struct T0 { + #[proptest(no_params)] + field: String +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +struct T1( + #[proptest(no_params)] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(params = "u8")] +struct T2 { + #[proptest(no_params)] + bar: String +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(params = "usize")] +struct T3( + #[proptest(no_params)] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +struct T4 { + #[proptest(params = "usize")] + baz: String +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +struct T5( + #[proptest(params = "String")] + String +); + +#[derive(Debug, Arbitrary)] // ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +enum T6 { + #[proptest(params = "String")] + V0(u8), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +enum T7 { + #[proptest(no_params)] + V0( + #[proptest(params = "String")] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +enum T8 { + V0( + #[proptest(params = "String")] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(params = "String")] +enum T9 { + V0( + #[proptest(no_params)] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(params = "String")] +enum T10 { + V0 { + #[proptest(no_params)] + batman: u8 + }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0010] +#[proptest(no_params)] +enum T11 { + V0 { + #[proptest(params = "String")] + batman: u8 + }, +} diff --git a/proptest-derive/tests/compile-fail/E0011-params-variant.rs b/proptest-derive/tests/compile-fail/E0011-params-variant.rs new file mode 100644 index 00000000..86fe1355 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0011-params-variant.rs @@ -0,0 +1,28 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0011] +enum T0 { + #[proptest(params = "String")] + V0( + #[proptest(no_params)] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0011] +enum T1 { + #[proptest(params = "(u8, u8)")] + V0 { + #[proptest(no_params)] + field: Vec + }, +} diff --git a/proptest-derive/tests/compile-fail/E0012-parent-has-filter.rs b/proptest-derive/tests/compile-fail/E0012-parent-has-filter.rs new file mode 100755 index 00000000..8663c19c --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0012-parent-has-filter.rs @@ -0,0 +1,224 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +fn even(x: &u8) -> bool { + x % 2 == 0 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0012] + //~| [proptest_derive, E0008] +enum NonFatal<#[proptest(skip)] T> { + #[proptest(strategy = "(0..10u8).prop_map(T0::V0)")] + V0( + #[proptest(filter(even))] + u8, + T + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T0 { + #[proptest(strategy = "(0..10u8).prop_map(T0::V0)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T1 { + #[proptest(strategy = "(0..10u8).prop_map(|field| T1::V0 { field })")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T2 { + #[proptest(value = "T2::V0(1)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T3 { + #[proptest(value = "T3::V0 { field: 1 }")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(no_params)] +enum T4 { + #[proptest(strategy = "(0..10u8).prop_map(T4::V0)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(no_params)] +enum T5 { + #[proptest(strategy = "(0..10u8).prop_map(|field| T5::V0 { field })")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(no_params)] +enum T6 { + #[proptest(value = "T6::V0(1)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(no_params)] +enum T7 { + #[proptest(value = "T7::V0 { field: 1 }")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +struct Unit; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(params(Unit))] +enum T8 { + #[proptest(strategy = "(0..10u8).prop_map(T8::V0)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(params(Unit))] +enum T9 { + #[proptest(strategy = "(0..10u8).prop_map(|field| T9::V0 { field })")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(params(Unit))] +enum T10 { + #[proptest(value = "T10::V0(1)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +#[proptest(params(Unit))] +enum T11 { + #[proptest(value = "T11::V0 { field: 1 }")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T12 { + #[proptest(params(Unit))] + #[proptest(strategy = "(0..10u8).prop_map(T12::V0)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T13 { + #[proptest(params(Unit))] + #[proptest(strategy = "(0..10u8).prop_map(|field| T13::V0 { field })")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T14 { + #[proptest(params(Unit))] + #[proptest(value = "T14::V0(1)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T15 { + #[proptest(params(Unit))] + #[proptest(value = "T15::V0 { field: 1 }")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T16 { + #[proptest(no_params)] + #[proptest(strategy = "(0..10u8).prop_map(T16::V0)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T17 { + #[proptest(no_params)] + #[proptest(strategy = "(0..10u8).prop_map(|field| T17::V0 { field })")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T18 { + #[proptest(no_params)] + #[proptest(value = "T18::V0(1)")] + V0( + #[proptest(filter(even))] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0012] +enum T19 { + #[proptest(no_params)] + #[proptest(value = "T19::V0 { field: 1 }")] + V0 { + #[proptest(filter(even))] + field: u8 + } +} \ No newline at end of file diff --git a/proptest-derive/tests/compile-fail/E0014-bare-attr.rs b/proptest-derive/tests/compile-fail/E0014-bare-attr.rs new file mode 100644 index 00000000..ffc897de --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0014-bare-attr.rs @@ -0,0 +1,75 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0014] + //~| [proptest_derive, E0007] +#[proptest] +#[proptest(value("foobar"))] +struct T0; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +#[proptest] +struct T1(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +#[proptest] +struct T2 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +struct T3 { + #[proptest] + field: Vec, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +struct T4( + #[proptest] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +#[proptest] +enum T5 { + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +enum T6 { + #[proptest] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +enum T7 { + V0 { + #[proptest] + foo: &'static str, + }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +enum T8 { + V0(#[proptest] bool) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +enum T9 { + #[proptest] + V0(bool), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0014] +enum T10 { + #[proptest] + V0 { bar: bool }, +} diff --git a/proptest-derive/tests/compile-fail/E0014-no-interp-meta.rs b/proptest-derive/tests/compile-fail/E0014-no-interp-meta.rs new file mode 100644 index 00000000..990d384c --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0014-no-interp-meta.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0024] + //~| [proptest_derive, E0008] +#[proptest ~~~] +#[proptest(skip)] +struct T0; diff --git a/proptest-derive/tests/compile-fail/E0015-lit-set.rs b/proptest-derive/tests/compile-fail/E0015-lit-set.rs new file mode 100644 index 00000000..5d7070c0 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0015-lit-set.rs @@ -0,0 +1,75 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ 2 errors: + //~| [proptest_derive, E0015] + //~| [proptest_derive, E0008] +#[proptest = 1] +#[proptest(skip)] +struct T0; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +#[proptest = 1] +struct T1(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +#[proptest = 1] +struct T2 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +struct T3 { + #[proptest = 1] + field: Vec, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +struct T4( + #[proptest = 1] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +#[proptest = 1] +enum T5 { + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +enum T6 { + #[proptest = 1] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +enum T7 { + V0 { + #[proptest = 1] + foo: &'static str, + }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +enum T8 { + V0(#[proptest = 1] bool) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +enum T9 { + #[proptest = 1] + V0(bool), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0015] +enum T10 { + #[proptest = 1] + V0 { bar: bool }, +} diff --git a/proptest-derive/tests/compile-fail/E0016-immediate-lit.rs b/proptest-derive/tests/compile-fail/E0016-immediate-lit.rs new file mode 100644 index 00000000..07e88479 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0016-immediate-lit.rs @@ -0,0 +1,75 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0016] + //~| [proptest_derive, E0008] +#[proptest(1)] +#[proptest(skip)] +struct T0; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +#[proptest(1)] +struct T1(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +#[proptest(1)] +struct T2 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +struct T3 { + #[proptest(1)] + field: Vec, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +struct T4( + #[proptest(1)] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +#[proptest(1)] +enum T5 { + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +enum T6 { + #[proptest(1)] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +enum T7 { + V0 { + #[proptest(1)] + foo: &'static str, + }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +enum T8 { + V0(#[proptest(1)] bool) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +enum T9 { + #[proptest(1)] + V0(bool), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0016] +enum T10 { + #[proptest(1)] + V0 { bar: bool }, +} diff --git a/proptest-derive/tests/compile-fail/E0017-set-again.rs b/proptest-derive/tests/compile-fail/E0017-set-again.rs new file mode 100644 index 00000000..32116528 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0017-set-again.rs @@ -0,0 +1,204 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0030] +#[proptest(no_params)] +#[proptest(no_params)] +struct T0; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0030] +#[proptest(no_params)] +#[proptest(no_params)] +struct T1(); + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0030] +#[proptest(no_params)] +#[proptest(no_params)] +struct T2 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +struct T3 { + #[proptest(no_params)] + #[proptest(no_params)] + field: Vec, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +struct T4( + #[proptest(no_params)] + #[proptest(no_params)] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +#[proptest(no_params)] +#[proptest(no_params)] +enum T5 { + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0029] +enum T6 { + #[proptest(no_params)] + #[proptest(no_params)] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T7 { + V0 { + #[proptest(no_params)] + #[proptest(no_params)] + foo: &'static str, + }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T8 { + V0(#[proptest(no_params)] #[proptest(no_params)] bool) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T9 { + #[proptest(no_params)] + #[proptest(no_params)] + V0(bool), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T10 { + #[proptest(no_params)] + #[proptest(no_params)] + V0 { bar: bool }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0030] +#[proptest(no_params, no_params)] +struct T11; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0030] +#[proptest(no_params, no_params)] +struct T12(); + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0030] +#[proptest(no_params, no_params)] +struct T13 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +struct T14 { + #[proptest(no_params, no_params)] + field: Vec, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +struct T15( + #[proptest(no_params, no_params)] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +#[proptest(no_params, no_params)] +enum T16 { + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| # [proptest_derive, E0017] + //~| # [proptest_derive, E0029] +enum T17 { + #[proptest(no_params, no_params)] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T18 { + V0 { + #[proptest(no_params, no_params)] + foo: &'static str, + }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T19 { + V0(#[proptest(no_params, no_params)] bool) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T20 { + #[proptest(no_params, no_params)] + V0(bool), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T21 { + #[proptest(no_params, no_params)] + V0 { bar: bool }, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T22 { + #[proptest(skip, skip)] + V0, + V1, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T23 { + V0, + #[proptest(w = 1, w = 2)] + V1, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +enum T24 { + V0, + #[proptest(weight = 1, weight = 2)] + V1, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +#[proptest(params = "String", params = "u8")] +enum T25 { + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +#[proptest(params = "String", params = "u8")] +struct T26 { + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +struct T27 { + #[proptest(value = "1", value = "3")] + field: u8, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0017] +#[proptest(no_bound, no_bound)] +struct T28 { + field: T, +} diff --git a/proptest-derive/tests/compile-fail/E0018-unknown-mod.rs b/proptest-derive/tests/compile-fail/E0018-unknown-mod.rs new file mode 100644 index 00000000..f7fc3b58 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0018-unknown-mod.rs @@ -0,0 +1,94 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +#[proptest(no_bounds)] +struct T0(T); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +enum T1 { + #[proptest(weights = 1)] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +enum T2 { + #[proptest(weighted = 1)] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +enum T3 { + V0( + #[proptest(strat = "1..0")] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +enum T4 { + V0( + #[proptest(strategies = "1..0")] + u8 + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +struct T5 { + #[proptest(values = "0")] + field: u8, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +struct T6 { + #[proptest(valued = "0")] + field: u8, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +struct T7 { + #[proptest(fix = "0")] + field: u8, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +struct T8 { + #[proptest(fixed = "0")] + field: u8, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +#[proptest(param = "u8")] +enum T9 { + V0(u8), +} + +// Show that E0018 is non-fatal. +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0018] + //~| [proptest_derive, E0011] +#[proptest(parameters = "u8")] +enum T10 { + #[proptest(params = "u8")] + V0(u8), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +#[proptest(no_param)] +struct T11; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +#[proptest(no_parameters)] +struct T12; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0018] +#[proptest(foobar)] +struct T13; diff --git a/proptest-derive/tests/compile-fail/E0019-no_params-malformed.rs b/proptest-derive/tests/compile-fail/E0019-no_params-malformed.rs new file mode 100644 index 00000000..c287e333 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0019-no_params-malformed.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0019] + //~| [proptest_derive, E0007] +#[proptest(no_params = 1, value("T0(u8)"))] +struct T0(u8); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0019] +#[proptest(no_params(2))] +struct T1(u8); diff --git a/proptest-derive/tests/compile-fail/E0020-skip-malformed.rs b/proptest-derive/tests/compile-fail/E0020-skip-malformed.rs new file mode 100644 index 00000000..7c929837 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0020-skip-malformed.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0020] + //~| [proptest_derive, E0007] +#[proptest(skip = 1, value = "T0(1)")] +struct T0(u8); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0020] +#[proptest(skip(2))] +struct T1(u8); diff --git a/proptest-derive/tests/compile-fail/E0021-weight-malformed.rs b/proptest-derive/tests/compile-fail/E0021-weight-malformed.rs new file mode 100644 index 00000000..23c7754b --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0021-weight-malformed.rs @@ -0,0 +1,60 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0021] + //~| [proptest_derive, E0008] +#[proptest(weight)] +#[proptest(skip)] +struct NonFatal; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T0 { + #[proptest(weight)] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T1 { + #[proptest(weight("abcd"))] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T2 { + #[proptest(weight("1.0"))] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T3 { + #[proptest(weight("true"))] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T4 { + #[proptest(weight = "true")] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T5 { + #[proptest(weight = true)] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0021] +enum T6 { + #[proptest(weight(true))] + V1 +} diff --git a/proptest-derive/tests/compile-fail/E0022-overspec-param.rs b/proptest-derive/tests/compile-fail/E0022-overspec-param.rs new file mode 100644 index 00000000..c0b96f68 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0022-overspec-param.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0022] +#[proptest(no_params, params = "u8")] +enum T0 { + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0022] +#[proptest(no_params, params = "u8")] +struct T1 { + field: String, +} diff --git a/proptest-derive/tests/compile-fail/E0023-params-malformed-expr.rs b/proptest-derive/tests/compile-fail/E0023-params-malformed-expr.rs new file mode 100644 index 00000000..1fe54fd7 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0023-params-malformed-expr.rs @@ -0,0 +1,74 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +#[proptest(params = "1/2")] +enum T0 { + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +#[proptest(params = ";;;")] +struct T1 { + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +enum T2 { + #[proptest(params = "Vec<1 + u8>")] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +enum T3 { + V1 { + #[proptest(params = "!!")] + field: Box, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +struct T4 { + #[proptest(params = "~")] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +#[proptest(params("1/2"))] +enum T5 { + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +#[proptest(params(";;;"))] +struct T6 { + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +enum T7 { + #[proptest(params("Vec<1 + u8>"))] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +enum T8 { + V1 { + #[proptest(params("!!"))] + field: Box, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +struct T9 { + #[proptest(params("~"))] + field: String, +} diff --git a/proptest-derive/tests/compile-fail/E0023-params-malformed.rs b/proptest-derive/tests/compile-fail/E0023-params-malformed.rs new file mode 100644 index 00000000..19f3d18e --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0023-params-malformed.rs @@ -0,0 +1,38 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0023] + //~| [proptest_derive, E0008] +#[proptest(skip)] +#[proptest(params)] +struct NonFatal; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +#[proptest(params)] +enum T0 { + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +enum T2 { + #[proptest(params)] + V1 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0023] +enum T3 { + V1 { + #[proptest(params)] + field: Box, + } +} diff --git a/proptest-derive/tests/compile-fail/E0025-overspec-strat.rs b/proptest-derive/tests/compile-fail/E0025-overspec-strat.rs new file mode 100644 index 00000000..43c8ac5b --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0025-overspec-strat.rs @@ -0,0 +1,42 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0025] +#[proptest(value = "T0(0)", strategy = "(0..6).prop_map(T1)")] +struct T0(u8); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0025] +struct T1 { + #[proptest(value = "1", strategy = "(0..1).prop_map(T1)")] + field: u8 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0025] +struct T2( + #[proptest(value = "1", strategy = "(0..1).prop_map(T1)")] + u8 +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0025] +enum T3 { + V0 { + #[proptest(value = "1", strategy = "0..1")] + field: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0025] +enum T4 { + V0( + #[proptest(value = "1", strategy = "0..1")] + u8 + ), +} diff --git a/proptest-derive/tests/compile-fail/E0026-strategy-malformed-expr.rs b/proptest-derive/tests/compile-fail/E0026-strategy-malformed-expr.rs new file mode 100644 index 00000000..f7492fd2 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0026-strategy-malformed-expr.rs @@ -0,0 +1,122 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T0 { + V1 { + #[proptest(strategy = "random garbage")] + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T1 { + V1( + #[proptest(strategy = "random garbage")] + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T2 { + #[proptest(strategy = "random garbage")] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T3( + #[proptest(strategy = "random garbage")] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T4 { + V1 { + #[proptest(value = "random garbage")] + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T5 { + V1( + #[proptest(value = "random garbage")] + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T6 { + #[proptest(value = "random garbage")] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T7( + #[proptest(value = "random garbage")] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T8 { + V1 { + #[proptest(strategy("random garbage"))] + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T9 { + V1( + #[proptest(strategy("random garbage"))] + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T10 { + #[proptest(strategy("random garbage"))] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T11( + #[proptest(strategy("random garbage"))] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T12 { + V1 { + #[proptest(value("random garbage"))] + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T13 { + V1( + #[proptest(value("random garbage"))] + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T14 { + #[proptest(value("random garbage"))] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T15( + #[proptest(value("random garbage"))] + String +); diff --git a/proptest-derive/tests/compile-fail/E0026-strategy-malformed.rs b/proptest-derive/tests/compile-fail/E0026-strategy-malformed.rs new file mode 100644 index 00000000..ac932209 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0026-strategy-malformed.rs @@ -0,0 +1,58 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0026] + //~| [proptest_derive, E0008] +#[proptest(strategy)] +#[proptest(skip)] +struct NonFatal; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T1 { + V1 { + #[proptest(strategy)] + batman: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T3 { + #[proptest(strategy("///"))] + field: usize, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T4( + #[proptest(strategy)] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +enum T5 { + V1 { + #[proptest(value)] + batman: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T6 { + #[proptest(value)] + field: usize, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0026] +struct T7( + #[proptest(value)] + usize, +); diff --git a/proptest-derive/tests/compile-fail/E0027-filter-malformed-expr.rs b/proptest-derive/tests/compile-fail/E0027-filter-malformed-expr.rs new file mode 100755 index 00000000..07ee63ff --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0027-filter-malformed-expr.rs @@ -0,0 +1,154 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T0 { + V1 { + #[proptest(filter = "random garbage")] + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T1 { + V1( + #[proptest(filter = "random garbage")] + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +struct T2 { + #[proptest(filter = "random garbage")] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +struct T3( + #[proptest(filter = "random garbage")] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T4 { + V1 { + #[proptest(filter("random garbage"))] + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T5 { + V1( + #[proptest(filter("random garbage"))] + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +struct T6 { + #[proptest(filter("random garbage"))] + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +struct T7( + #[proptest(filter("random garbage"))] + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T8 { + #[proptest(filter = "random garbage")] + V1 { + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T9 { + #[proptest(filter = "random garbage")] + V1( + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter = "random garbage")] +struct T10 { + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter = "random garbage")] +struct T11( + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T12 { + #[proptest(filter("random garbage"))] + V1 { + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T13 { + #[proptest(filter("random garbage"))] + V1( + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter("random garbage"))] +struct T14 { + field: String, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter("random garbage"))] +struct T15( + String +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter = "random garbage")] +enum T16 { + V1 { + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter = "random garbage")] +enum T17 { + V1( + u8, + ), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter("random garbage"))] +enum T18 { + V1 { + field: u8, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter("random garbage"))] +enum T19 { + V1( + u8, + ), +} \ No newline at end of file diff --git a/proptest-derive/tests/compile-fail/E0027-filter-malformed.rs b/proptest-derive/tests/compile-fail/E0027-filter-malformed.rs new file mode 100755 index 00000000..9b756bb1 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0027-filter-malformed.rs @@ -0,0 +1,82 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0027] + //~| [proptest_derive, E0008] +#[proptest(filter)] +#[proptest(skip)] +struct NonFatal; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +struct T0 { + #[proptest(filter)] + field: usize, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +struct T1( + #[proptest(filter)] + usize, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T2 { + V1 { + #[proptest(filter)] + batman: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T3 { + V1( + #[proptest(filter)] + u8 + ) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T4 { + #[proptest(filter)] + V1 { + batman: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +enum T5 { + #[proptest(filter)] + V1(u8) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter)] +enum T6 { + V1 { + batman: u8 + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter)] +enum T7 { + V1(u8) +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter = 1)] +struct T8(u8); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0027] +#[proptest(filter(1))] +struct T9(u8); \ No newline at end of file diff --git a/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-filter.rs b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-filter.rs new file mode 100755 index 00000000..019fe66a --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-filter.rs @@ -0,0 +1,123 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0028] + //~| [proptest_derive, E0006] +enum NonFatal { + #[proptest(skip, filter(foo))] + V1(u8), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T0 { + #[proptest(skip, filter(foo))] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T1 { + #[proptest( + skip, + filter(foo) + )] + V1 { + field: u8 + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T2 { + #[proptest(skip)] + V1( + #[proptest(filter(foo))] + u8 + ), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T3 { + #[proptest(skip)] + V1 { + #[proptest(filter(foo))] + field: u8 + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T4 { + #[proptest(skip, filter(foo))] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T5 { + #[proptest(skip, filter(foo))] + V1 { + field: u8 + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T6 { + #[proptest(skip)] + V1( + #[proptest(filter(foo))] + u8 + ), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T7 { + #[proptest(skip)] + V1 { + #[proptest(filter(foo))] + field: usize + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T8 { + #[proptest(skip)] + V1 { + #[proptest(filter(foo))] + field: usize + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T9 { + #[proptest(skip)] + V1 { + #[proptest(filter(foo))] + field: usize + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T10 { + #[proptest(skip)] + V1 { + #[proptest(filter(foo))] + field: usize + }, + V2, +} diff --git a/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-param.rs b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-param.rs new file mode 100644 index 00000000..0f31a287 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-param.rs @@ -0,0 +1,84 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0028] + //~| [proptest_derive, E0006] +enum NonFatal { + #[proptest(skip, no_params)] + V1, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T0 { + #[proptest(skip, no_params)] + V1, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T1 { + #[proptest(skip, no_params)] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T2 { + #[proptest(skip, no_params)] + V1 { + field: String, + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T3 { + #[proptest(skip, params = "u8")] + V1, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T4 { + #[proptest(skip, params = "u8")] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T5 { + #[proptest(skip, params = "u8")] + V1 { + field: String, + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T6 { + #[proptest(skip)] + V1( + #[proptest(params = "u8")] + u8 + ), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T7 { + #[proptest(skip)] + V1 { + #[proptest(params = "u8")] + field: String, + }, + V2, +} diff --git a/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-strat.rs b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-strat.rs new file mode 100644 index 00000000..d9d9e70e --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-strat.rs @@ -0,0 +1,123 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0028] + //~| [proptest_derive, E0006] +enum NonFatal { + #[proptest(skip, strategy = "(0..10).prop_map(NonFatal::V1)")] + V1(u8), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T0 { + #[proptest(skip, strategy = "(0..10).prop_map(T0::V1)")] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T1 { + #[proptest( + skip, + strategy = "(0..10).prop_map(|field| T0::V1 { field })" + )] + V1 { + field: u8 + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T2 { + #[proptest(skip)] + V1( + #[proptest(strategy = "0..10")] + u8 + ), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T3 { + #[proptest(skip)] + V1 { + #[proptest(strategy = "0..10")] + field: u8 + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T4 { + #[proptest(skip, value = "T0::V1(1)")] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T5 { + #[proptest(skip, value = "T0::V1 { field: 3 }")] + V1 { + field: u8 + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T6 { + #[proptest(skip)] + V1( + #[proptest(value = "42")] + u8 + ), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T7 { + #[proptest(skip)] + V1 { + #[proptest(value = "1337")] + field: usize + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T8 { + #[proptest(skip)] + V1 { + #[proptest(value("1337"))] + field: usize + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T9 { + #[proptest(skip)] + V1 { + #[proptest(value(1337))] + field: usize + }, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T10 { + #[proptest(skip)] + V1 { + #[proptest(value = 1337)] + field: usize + }, + V2, +} diff --git a/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-weight.rs b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-weight.rs new file mode 100644 index 00000000..bf90e4f6 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0028-skipped-variant-has-weight.rs @@ -0,0 +1,41 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0028] + //~| [proptest_derive, E0006] +enum NonFatal { + #[proptest(skip, weight = 2)] + V1, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T0 { + #[proptest(skip, weight = 2)] + V1, + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T1 { + #[proptest(skip, w = 3)] + V1(u8), + V2, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0028] +enum T2 { + #[proptest(skip, w = 3)] + V1 { + field: String, + }, + V2, +} diff --git a/proptest-derive/tests/compile-fail/E0029-filter-on-unit-variant.rs b/proptest-derive/tests/compile-fail/E0029-filter-on-unit-variant.rs new file mode 100755 index 00000000..19a2cdd4 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0029-filter-on-unit-variant.rs @@ -0,0 +1,58 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0029] + //~| [proptest_derive, E0008] +enum NonFatal { + #[proptest(filter(foo))] + V0, + V1 { + #[proptest(skip)] + field: usize, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T0 { + #[proptest(filter(foo))] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T1 { + #[proptest(filter(foo))] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T2 { + #[proptest(filter(foo))] + V0 {}, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T3 { + #[proptest(filter(foo))] + V0 {}, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T4 { + #[proptest(filter(foo))] + V0(), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T5 { + #[proptest(filter(foo))] + V0(), +} diff --git a/proptest-derive/tests/compile-fail/E0029-params-on-unit-variant.rs b/proptest-derive/tests/compile-fail/E0029-params-on-unit-variant.rs new file mode 100644 index 00000000..44860d05 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0029-params-on-unit-variant.rs @@ -0,0 +1,58 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0029] + //~| [proptest_derive, E0008] +enum NonFatal { + #[proptest(params = "u8")] + V0, + V1 { + #[proptest(skip)] + field: usize, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T0 { + #[proptest(no_params)] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T1 { + #[proptest(params = "u8")] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T2 { + #[proptest(no_params)] + V0 {}, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T3 { + #[proptest(params = "u8")] + V0 {}, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T4 { + #[proptest(no_params)] + V0(), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T5 { + #[proptest(params = "u8")] + V0(), +} diff --git a/proptest-derive/tests/compile-fail/E0029-strategy-on-unit-variant.rs b/proptest-derive/tests/compile-fail/E0029-strategy-on-unit-variant.rs new file mode 100644 index 00000000..58677bbc --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0029-strategy-on-unit-variant.rs @@ -0,0 +1,58 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0029] + //~| [proptest_derive, E0008] +enum NonFatal { + #[proptest(strategy = "Just(T0::V0)")] + V0, + V1 { + #[proptest(skip)] + field: usize, + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T0 { + #[proptest(strategy = "Just(T0::V0)")] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T1 { + #[proptest(value = "T0::V0")] + V0, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T2 { + #[proptest(strategy = "Just(T0::V0)")] + V0 {}, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T3 { + #[proptest(value = "T0::V0")] + V0 {}, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T4 { + #[proptest(strategy = "Just(T0::V0)")] + V0(), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0029] +enum T5 { + #[proptest(value = "T0::V0")] + V0(), +} diff --git a/proptest-derive/tests/compile-fail/E0030-params-on-unit-struct.rs b/proptest-derive/tests/compile-fail/E0030-params-on-unit-struct.rs new file mode 100644 index 00000000..a93185e2 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0030-params-on-unit-struct.rs @@ -0,0 +1,52 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// It happens that no other error will follow E0030 so this is not as proper +// a check that we wanted to ensure that E0030 is non-fatal. + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors + //~| [proptest_derive, E0008] + //~| [proptest_derive, E0030] +#[proptest(params = "u8")] +#[proptest(skip)] +struct T0; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(no_params)] +struct T1; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(params = "u8")] +struct T2 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(no_params)] +struct T3 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(params = "u8")] +struct T4(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(no_params)] +struct T5(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(filter(foo))] +struct T6; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(filter(foo))] +struct T7(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0030] +#[proptest(filter(foo))] +struct T8 {} \ No newline at end of file diff --git a/proptest-derive/tests/compile-fail/E0031-no-bound-non-tyvar.rs b/proptest-derive/tests/compile-fail/E0031-no-bound-non-tyvar.rs new file mode 100644 index 00000000..43dcabf0 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0031-no-bound-non-tyvar.rs @@ -0,0 +1,47 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0031] + //~| [proptest_derive, E0008] +struct T1 { + #[proptest(no_bound)] + #[proptest(skip)] + field: ::std::marker::PhantomData, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0031] +struct T2( + #[proptest(no_bound)] + ::std::marker::PhantomData, +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0031] +enum T3 { + #[proptest(no_bound)] + V1(T), +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0031] +enum T4 { + #[proptest(no_bound)] + V1 { + field: T + } +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0031] +enum T5 { + V1( + #[proptest(no_bound)] + T + ), +} diff --git a/proptest-derive/tests/compile-fail/E0032-no-bound-malformed.rs b/proptest-derive/tests/compile-fail/E0032-no-bound-malformed.rs new file mode 100644 index 00000000..960699d8 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0032-no-bound-malformed.rs @@ -0,0 +1,93 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0032] + //~| [proptest_derive, E0007] +#[proptest(no_bound = "...", value("TU0"))] +struct TU0; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound("..."))] +struct TU1; + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound = "...")] +struct TU2 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound("..."))] +struct TU3 {} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound = "...")] +struct TU4(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound("..."))] +struct TU5(); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound = "...")] +struct T0 { + field: u8 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound("..."))] +struct T1 { + field: u8 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound = "...")] +struct T2(u8); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound("..."))] +struct T3(u8); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +struct T4 { + #[proptest(no_bound = "...")] + field: u8 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +struct T5 { + #[proptest(no_bound("..."))] + field: u8 +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +struct T6( + #[proptest(no_bound = "...")] + u8 +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +struct T7( + #[proptest(no_bound("..."))] + u8 +); + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +#[proptest(no_bound("..."))] +enum T8 { + V1, +} + +#[derive(Debug, Arbitrary)] //~ ERROR: [proptest_derive, E0032] +enum T9 { + #[proptest(no_bound("..."))] + V1, +} diff --git a/proptest-derive/tests/compile-fail/E0033-weight-overflow.rs b/proptest-derive/tests/compile-fail/E0033-weight-overflow.rs new file mode 100755 index 00000000..e8e5ab21 --- /dev/null +++ b/proptest-derive/tests/compile-fail/E0033-weight-overflow.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +// Show non-fatal: +#[derive(Debug, Arbitrary)] //~ ERROR: 2 errors: + //~| [proptest_derive, E0033] + //~| [proptest_derive, E0008] +enum T0<#[proptest(skip)] T> { + #[proptest(weight = 4294967290)] + V0(T), + #[proptest(weight = 5)] + V1, + V2, +} diff --git a/proptest-derive/tests/compile-fail/must-be-debug.rs b/proptest-derive/tests/compile-fail/must-be-debug.rs new file mode 100644 index 00000000..4f41e4e2 --- /dev/null +++ b/proptest-derive/tests/compile-fail/must-be-debug.rs @@ -0,0 +1,13 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +#[derive(Arbitrary)] //~ `Foo` doesn't implement `std::fmt::Debug` [E0277] +struct Foo { x: usize } diff --git a/proptest-derive/tests/compile-fail/no-arbitrary.rs b/proptest-derive/tests/compile-fail/no-arbitrary.rs new file mode 100644 index 00000000..9d46dfec --- /dev/null +++ b/proptest-derive/tests/compile-fail/no-arbitrary.rs @@ -0,0 +1,15 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; + +struct T0; + +#[derive(Debug, Arbitrary)] //~ Arbitrary` is not satisfied [E0277] +struct T1 { f0: T0, } diff --git a/proptest-derive/tests/compiletest.rs b/proptest-derive/tests/compiletest.rs new file mode 100644 index 00000000..c5e95af9 --- /dev/null +++ b/proptest-derive/tests/compiletest.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate compiletest_rs as ct; + +use std::env; + +fn run_mode(src: &'static str, mode: &'static str) { + let mut config = ct::Config::default(); + + config.mode = mode.parse().expect("invalid mode"); + config.target_rustcflags = Some("-L target/debug/deps".to_owned()); + if let Ok(name) = env::var("TESTNAME") { + config.filter = Some(name); + } + config.src_base = format!("tests/{}", src).into(); + + ct::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail", "compile-fail"); +} diff --git a/proptest-derive/tests/enum.rs b/proptest-derive/tests/enum.rs new file mode 100644 index 00000000..be37b13e --- /dev/null +++ b/proptest-derive/tests/enum.rs @@ -0,0 +1,199 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +#[macro_use] +extern crate proptest_derive; +extern crate proptest; +use proptest::prelude::Arbitrary; + +#[derive(Debug, Arbitrary)] +enum T1 { + V1, +} + +#[derive(Debug, Arbitrary)] +enum T2 { + V1(), V2 {}, +} + +#[derive(Debug, Arbitrary)] +enum T3 { + V1(), V2 {}, V3, +} + +#[derive(Debug, Arbitrary)] +enum T4 { + V1, V2(), V3, V4 {}, +} + +#[derive(Debug, Arbitrary)] +enum T5 { + V1, V2, V3, V4 {}, V5(), +} + +#[derive(Debug, Arbitrary)] +enum T6 { + V1(), V2, V3 {}, V4, V5, V6, +} + +#[derive(Debug, Arbitrary)] +enum T7 { + V1, V2, V3, V4 {}, V5, V6, V7(), +} + +#[derive(Debug, Arbitrary)] +enum T8 { + V1, V2, V3(), V4, V5, V6{}, V7, V8, +} + +#[derive(Debug, Arbitrary)] +enum T9 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, +} + +#[derive(Debug, Arbitrary)] +enum T10 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, +} + +#[derive(Debug, Arbitrary)] +enum T11 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, +} + +#[derive(Debug, Arbitrary)] +enum T12 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, +} + +#[derive(Debug, Arbitrary)] +enum T13 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, +} + +#[derive(Debug, Arbitrary)] +enum T14 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, +} + +#[derive(Debug, Arbitrary)] +enum T15 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, +} + +#[derive(Debug, Arbitrary)] +enum T16 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, +} + +#[derive(Debug, Arbitrary)] +enum T17 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, +} + +#[derive(Debug, Arbitrary)] +enum T18 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, +} + +#[derive(Debug, Arbitrary)] +enum T19 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19 +} + +#[derive(Debug, Arbitrary)] +enum T20 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20 +} + +#[derive(Debug, Arbitrary)] +enum T21 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, +} + +#[derive(Debug, Arbitrary)] +enum T22 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, V22, +} + +#[derive(Debug, Arbitrary)] +enum T23 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, V22, V23, +} + +#[derive(Debug, Arbitrary)] +enum T24 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, V22, V23, V24, +} + +#[derive(Debug, Arbitrary)] +enum T25 { + V1, V2, V3, V4, V5 {}, V6(), V7, V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, +} + +#[derive(Clone, Debug, Arbitrary)] +enum Alan { + A(usize), + B(String), + C(()), + D(u32), + E(f64), + F(char) +} + +#[derive(Clone, Debug, Arbitrary)] +enum SameType { + A(usize), + B(usize), +} + + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/filter.rs b/proptest-derive/tests/filter.rs new file mode 100755 index 00000000..9180ae38 --- /dev/null +++ b/proptest-derive/tests/filter.rs @@ -0,0 +1,237 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::*; + +fn even(x: &usize) -> bool { + x % 2 == 0 +} + +fn rem3(x: &usize) -> bool { + x % 3 == 0 +} + +#[derive(Copy, Clone)] +struct Param(usize); + +impl Default for Param { + fn default() -> Self { + Param(100) + } +} + +#[derive(Debug, Arbitrary)] +#[proptest(filter("|x| x.foo % 3 == 0"))] +struct T0 { + #[proptest(no_params, filter(even))] + foo: usize, + #[proptest(filter("|x| x % 2 == 1"))] + bar: usize, + #[proptest(strategy = "0..100usize", filter = "|x| x % 2 == 1")] + baz: usize, + #[proptest(value = "42", filter(even))] + quux: usize, + #[proptest(params(Param), strategy("0..=params.0"), filter("|x| *x > 2"))] + wibble: usize, +} + +#[derive(Debug, Arbitrary)] +#[proptest(params(Param))] +#[proptest(filter("|x| x.foo % 3 == 0"))] +struct T1 { + #[proptest(filter(even))] + foo: usize, + #[proptest(filter("|x| x % 2 == 1"))] + bar: usize, + #[proptest(strategy = "0..100usize", filter = "|x| x % 2 == 1")] + baz: usize, + #[proptest(value = "42", filter(even))] + quux: usize, + #[proptest(strategy("0..=params.0"), filter("|x| *x > 2"))] + wibble: usize, +} + +#[derive(Debug, Arbitrary)] +#[proptest(filter("|x| x.0 % 3 == 0"))] +struct T2( + #[proptest(no_params, filter(even))] + usize, + #[proptest(filter("|x| x % 2 == 1"))] + usize, + #[proptest(strategy = "0..100usize", filter = "|x| x % 2 == 1")] + usize, + #[proptest(value = "42", filter(even))] + usize, + #[proptest(params(Param), strategy("0..=params.0"), filter("|x| *x > 2"))] + usize, +); + +#[derive(Debug, Arbitrary)] +#[proptest(filter("|x| x.0 % 3 == 0"))] +struct T3( + #[proptest(no_params, filter(even))] + usize, + #[proptest(filter("|x| x % 2 == 1"))] + usize, + #[proptest(strategy = "0..100usize", filter = "|x| x % 2 == 1")] + usize, + #[proptest(value = "42", filter(even))] + usize, + #[proptest(params(Param), strategy("0..=params.0"), filter("|x| *x > 2"))] + usize, +); + +fn is_v0(v: &T4) -> bool { + if let T4::V0 { .. } = v { true } else { false } +} + +#[derive(Debug, Arbitrary)] +#[proptest(filter(is_v0))] +enum T4 { + V0 { + #[proptest(filter(even))] + field: usize + }, + V1 +} + +fn t5_v0_rem_3(v: &T5) -> bool { + if let T5::V0 { field } = v { rem3(&field) } else { false } +} + +fn t5_v1_rem_5(v: &T5) -> bool { + if let T5::V1(field) = v { field % 5 == 0 } else { false } +} + +#[derive(Debug, Arbitrary)] +enum T5 { + #[proptest(filter(t5_v0_rem_3))] + V0 { + #[proptest(filter(even))] + field: usize, + }, + #[proptest( + strategy("(0..1000usize).prop_map(T5::V1)"), + filter(t5_v1_rem_5) + )] + V1(usize), +} + +fn t6_v0_rem_3(v: &T6) -> bool { + if let T6::V0 { field } = v { rem3(&field) } else { false } +} + +fn t6_v1_rem_5(v: &T6) -> bool { + if let T6::V1(field) = v { field % 5 == 0 } else { false } +} + +#[derive(Debug, Arbitrary)] +#[proptest(params(Param))] +enum T6 { + #[proptest(filter(t6_v0_rem_3))] + V0 { + #[proptest(filter(even))] + field: usize, + }, + #[proptest( + strategy("(0..params.0).prop_map(T6::V1)"), + filter(t6_v1_rem_5) + )] + V1(usize), +} + +#[derive(Debug, Arbitrary)] +struct T7 { + #[proptest(filter(even), filter(rem3))] + foo: usize, +} + +proptest! { + #[test] + fn t0_test(v: T0) { + assert!(even(&v.foo) && rem3(&v.foo)); + assert!(!even(&v.bar)); + assert!(!even(&v.baz) && v.baz < 100); + assert!(even(&v.quux) && v.quux == 42); + assert!(even(&v.quux) && v.quux == 42); + assert!(v.wibble > 2 && v.wibble <= 100); + } + + #[test] + fn t1_test(v: T1) { + assert!(even(&v.foo) && v.foo % 3 == 0); + assert!(!even(&v.bar)); + assert!(!even(&v.baz) && v.baz < 100); + assert!(even(&v.quux) && v.quux == 42); + assert!(v.wibble > 2 && v.wibble <= 100); + } + + #[test] + fn t2_test(v: T2) { + assert!(even(&v.0) && v.0 % 3 == 0); + assert!(!even(&v.1)); + assert!(!even(&v.2) && v.2 < 100); + assert!(even(&v.3) && v.3 == 42); + assert!(v.4 > 2 && v.4 <= 100); + } + + #[test] + fn t3_test(v: T3) { + assert!(even(&v.0) && v.0 % 3 == 0); + assert!(!even(&v.1)); + assert!(!even(&v.2) && v.2 < 100); + assert!(even(&v.3) && v.3 == 42); + assert!(v.4 > 2 && v.4 <= 100); + } + + #[test] + fn t4_test(v: T4) { + assert!(if let T4::V0 { field } = v { even(&field) } else { false }); + } + + #[test] + fn t5_test(v: T5) { + match v { + T5::V0 { field } => assert!(rem3(&field) && even(&field)), + T5::V1(field) => assert!(field < 1000 && field % 5 == 0), + } + } + + #[test] + fn t6_test(v: T6) { + match v { + T6::V0 { field } => assert!(rem3(&field) && even(&field)), + T6::V1(field) => assert!(field < 100 && field % 5 == 0), + } + } + + #[test] + fn t7_test(v: T7) { + assert!(even(&v.foo) && rem3(&v.foo)); + } +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/misc.rs b/proptest-derive/tests/misc.rs new file mode 100644 index 00000000..770f72a2 --- /dev/null +++ b/proptest-derive/tests/misc.rs @@ -0,0 +1,80 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] + +#[macro_use] +extern crate proptest_derive; +extern crate proptest; +use proptest::strategy::Just; +use proptest::prelude::Arbitrary; + +// TODO: An idea. +/* +#[derive(Debug, Arbitrary)] +#[proptest(with = "Foo::ctor(1337, :usize:.other_fn(:f64:, #0..7#))")] +struct Foo { + //.. +} +*/ + +#[derive(Default)] +struct Complex; + +#[derive(Debug, Arbitrary)] +#[proptest(params(Complex))] +enum Foo { + #[proptest(value = "Foo::F0(1, 1)")] + F0(usize, u8), +} + +#[derive(Clone, Debug, Arbitrary)] +#[proptest(params = "usize")] +enum A { + B, + #[proptest(strategy = "Just(A::C(1))")] + C(usize) +} + +#[derive(Clone, Debug, Arbitrary)] +enum Bobby { + #[proptest(no_params)] + B(usize), + #[proptest(no_params, value = "Bobby::C(1)")] + C(usize), + #[proptest(no_params, strategy = "Just(Bobby::D(1))")] + D(usize), + #[proptest(params(Complex), value = "Bobby::E(1)")] + E(usize), + #[proptest(params(Complex), strategy = "Just(Bobby::F(1))")] + F(usize), +} + +#[derive(Clone, Debug, Arbitrary)] +enum Quux { + B(#[proptest(no_params)] usize), + C(usize, String), + #[proptest(value = "Quux::D(2, \"a\".into())")] + D(usize, String), + #[proptest(strategy = "Just(Quux::E(1337))")] + E(u32), + F { + #[proptest(strategy = "10usize..20usize")] + foo: usize + } +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/no_bound.rs b/proptest-derive/tests/no_bound.rs new file mode 100644 index 00000000..04fc99ee --- /dev/null +++ b/proptest-derive/tests/no_bound.rs @@ -0,0 +1,60 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; +extern crate proptest; +use proptest::prelude::Arbitrary; + +#[derive(Debug)] +struct NotArbitrary; + +/// Ensure that we can't determine that this is PhantomData syntactically. +type HidePH = ::std::marker::PhantomData; + +/* +// TODO handle this... + +#[derive(Debug, Arbitrary)] +struct T1<#[proptest(no_bound)] T>(HidePH); + +#[derive(Debug, Arbitrary)] +struct T2(T1); + +#[derive(Debug, Arbitrary)] +struct T3< + #[proptest(no_bound)] A, + B, + #[proptest(no_bound)] G, +> { + alpha: HidePH, + beta: B, + gamma: HidePH, +} + +#[derive(Debug, Arbitrary)] +struct T4(T3); +*/ + +#[derive(Debug, Arbitrary)] +#[proptest(no_bound)] +struct T5(HidePH<(A, B, C)>); + +#[derive(Debug, Arbitrary)] +struct T6(T5); + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + /* + assert_arbitrary::(); + assert_arbitrary::(); + */ + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/params.rs b/proptest-derive/tests/params.rs new file mode 100644 index 00000000..5eaf72d1 --- /dev/null +++ b/proptest-derive/tests/params.rs @@ -0,0 +1,123 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::{any_with, Arbitrary}; + +struct ComplexType { + max: u64, +} + +impl Default for ComplexType { + fn default() -> Self { Self { max: 10 } } +} + +#[derive(Debug, Arbitrary)] +#[proptest(params(ComplexType))] +struct TopHasParams { + string: usize, + #[proptest(strategy = "0..params.max")] + int: u64, +} + +#[derive(Debug, Arbitrary)] +#[proptest(no_params)] +struct TopNoParams { + stuff: usize, +} + +#[derive(Debug, Arbitrary)] +struct InnerNoParams { + string: String, + #[proptest(no_params)] + has: TopHasParams, +} + +#[derive(Debug, Arbitrary)] +#[proptest(params(u64))] +struct TPIS { + #[proptest(strategy = "\"a+\"")] + string: String, + #[proptest(strategy = "3..=params")] + int: u64, +} + +#[derive(Debug, Arbitrary)] +struct Parallel { + #[proptest(params = "&'static str", strategy = "params")] + string: String, + #[proptest(params(u8), strategy = "0i64..params as i64")] + int: i64, +} + +#[derive(Debug, Arbitrary)] +struct Parallel2 { + #[proptest(params("&'static str"), strategy = "params")] + string: String, + #[proptest(params("u8"), strategy = "0i64..params as i64")] + int: i64, +} + +const MAX: ComplexType = ComplexType { max: 5, }; + +proptest! { + #[test] + fn top_has_params(v in any_with::(MAX)) { + prop_assert!(v.int < 5); + } + + #[test] + fn top_no_params(_ in any_with::(())) {} + + #[test] + fn inner_params(inner in any_with::("\\s+".into())) { + prop_assert!(inner.has.int < 10); + prop_assert!(inner.string.trim().is_empty()); + } + + #[test] + fn top_param_inner_strat(inner in any_with::(6)) { + prop_assert!(inner.int <= 6); + prop_assert!(inner.int >= 3); + prop_assert_eq!( + 0, + inner.string.split("a").filter(|s| !s.is_empty()).count() + ); + } + + #[test] + fn parallel_params(inner in any_with::(("[0-9]", 3))) { + prop_assert!(inner.int >= 0); + prop_assert!(inner.int < 3); + prop_assert!(inner.string.chars().next().unwrap().is_digit(10)); + } + + #[test] + fn parallel_params2(inner in any_with::(("[0-9]", 3))) { + prop_assert!(inner.int >= 0); + prop_assert!(inner.int < 3); + prop_assert!(inner.string.chars().next().unwrap().is_digit(10)); + } +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/phantom.rs b/proptest-derive/tests/phantom.rs new file mode 100644 index 00000000..7f1ccf61 --- /dev/null +++ b/proptest-derive/tests/phantom.rs @@ -0,0 +1,64 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; +extern crate proptest; +use proptest::prelude::Arbitrary; + +use std::marker; +use std::marker::PhantomData; + +#[derive(Debug)] +struct NotArbitrary; + +#[derive(Debug, Arbitrary)] +struct T1(::std::marker::PhantomData); + +#[derive(Debug, Arbitrary)] +struct T2(T1); + +#[derive(Debug, Arbitrary)] +struct T3(marker::PhantomData); + +#[derive(Debug, Arbitrary)] +struct T4(T3); + +#[derive(Debug, Arbitrary)] +struct T5(PhantomData); + +#[derive(Debug, Arbitrary)] +struct T6(T5); + +#[derive(Debug, Arbitrary)] +struct T7(std::marker::PhantomData); + +#[derive(Debug, Arbitrary)] +struct T8(T7); + +#[derive(Debug, Arbitrary)] +struct T9 { + a: A, + b: B, + c: PhantomData, +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::>(); + assert_arbitrary::(); + assert_arbitrary::>(); + assert_arbitrary::(); + assert_arbitrary::>(); + assert_arbitrary::(); + assert_arbitrary::>(); + assert_arbitrary::(); + assert_arbitrary::>(); +} diff --git a/proptest-derive/tests/skip.rs b/proptest-derive/tests/skip.rs new file mode 100644 index 00000000..4b7b3715 --- /dev/null +++ b/proptest-derive/tests/skip.rs @@ -0,0 +1,54 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] +#![allow(dead_code, unreachable_code)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::Arbitrary; + +#[derive(Debug, Arbitrary, PartialEq)] +enum Ty1 { + V1, + V2(!), + #[proptest(skip)] + V3, +} + +#[derive(Debug, Arbitrary, PartialEq)] +enum Ty2 { + V1, + V2, + #[proptest(skip)] + V3, + #[proptest(skip)] + V4, +} + +proptest! { + #[test] + fn ty1_always_v1(v: Ty1) { + prop_assert_eq!(v, Ty1::V1); + } + + #[test] + fn ty_always_1_or_2(v: Ty2) { + prop_assert!(v == Ty2::V1 || v == Ty2::V2); + } +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/strategy.rs b/proptest-derive/tests/strategy.rs new file mode 100755 index 00000000..b478e211 --- /dev/null +++ b/proptest-derive/tests/strategy.rs @@ -0,0 +1,112 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::{Strategy, Arbitrary}; + +fn make_strategy(start: usize) -> impl Strategy { + (start..100).prop_map(|x| x * 2) +} + +fn make_strategy2() -> impl Strategy { + make_strategy(88) +} + +#[derive(Debug, Arbitrary)] +struct T0 { + #[proptest(strategy = "make_strategy(0)")] + foo: usize, + #[proptest(strategy("make_strategy(11)"))] + bar: usize, + #[proptest(strategy(make_strategy2))] + baz: usize, +} + +#[derive(Debug, Arbitrary)] +struct T1( + #[proptest(strategy = "make_strategy(22)")] + usize, + #[proptest(strategy("make_strategy(33)"))] + usize, + #[proptest(strategy(make_strategy2))] + usize, +); + +#[derive(Debug, Arbitrary)] +enum T2 { + V0( + #[proptest(strategy("make_strategy(44)"))] + usize + ), + V1 { + #[proptest(strategy = "make_strategy(55)")] + field: usize, + }, + V2( + #[proptest(strategy = "make_strategy(66)")] + usize + ), + V3 { + #[proptest(strategy("make_strategy(77)"))] + field: usize, + }, + V4( + #[proptest(strategy(make_strategy2))] + usize + ), + V5 { + #[proptest(strategy(make_strategy2))] + field: usize + }, +} + +fn assert_consistency(start: usize, val: usize) { + assert!(val % 2 == 0 && val < 200 && val >= (start * 2)); +} + +proptest! { + #[test] + fn t0_test(v: T0) { + assert_consistency(0, v.foo); + assert_consistency(11, v.bar); + assert_consistency(88, v.baz); + } + + #[test] + fn t1_test(v: T1) { + assert_consistency(22, v.0); + assert_consistency(33, v.1); + assert_consistency(88, v.2); + } + + #[test] + fn t2_test(v: T2) { + match v { + T2::V0(v) => assert_consistency(44, v), + T2::V1 { field } => assert_consistency(55, field), + T2::V2(v) => assert_consistency(66, v), + T2::V3 { field } => assert_consistency(77, field), + T2::V4(v) => assert_consistency(88, v), + T2::V5 { field } => assert_consistency(88, field), + } + } +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} \ No newline at end of file diff --git a/proptest-derive/tests/uninhabited-pass.rs b/proptest-derive/tests/uninhabited-pass.rs new file mode 100644 index 00000000..fdf8be31 --- /dev/null +++ b/proptest-derive/tests/uninhabited-pass.rs @@ -0,0 +1,98 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] +#![allow(dead_code, unreachable_code)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::Arbitrary; + +// Various arithmetic and basic things. +#[derive(Debug, Arbitrary, PartialEq)] +enum Ty1 { + // Ensure that all of the types below are deemed uninhabited: + V2(!), + V3([!; 1]), + V4([!; 2 - 1]), + V5([!; 2 * 1]), + V6([!; 2 / 2]), + V7([!; 0b0 ^ 0b1]), + V8([!; 0b1 & 0b1]), + V9([!; 0b1 | 0b0]), + V10([!; 0b10 << 1]), + V11([!; 0b10 >> 1]), + V12([!; !0 - 18446744073709551614]), + V13([!; (1 + 2 * (3 / 3))]), + // This one is inhabited: + #[warn(dead_code)] + V1, +} + +proptest! { + #[test] + fn ty1_always_v1(v1: Ty1) { + prop_assert_eq!(v1, Ty1::V1); + } +} + +// Can't inspect type macros called as mac!(uninhabited_type). +macro_rules! tymac { + ($ignore: ty) => { u8 }; +} + +#[derive(Debug, Arbitrary)] +struct TyMac0 { + field: tymac!(!), +} + +#[derive(Debug, Arbitrary)] +struct TyMac1 { + baz: tymac!([!; 3 + 4]), +} + +enum TyMac2 { + #[deny(dead_code)] + V0(tymac!((u8, !, usize))) +} + +// Can't inspect projections through associated types: +trait Fun { + type Prj; +} +impl Fun for ! { + type Prj = u8; +} +impl Fun for (!, usize, !) { + type Prj = u8; +} + +#[derive(Debug, Arbitrary)] +enum UsePrj0 { + #[deny(dead_code)] + V0(::Prj) +} + +#[derive(Debug, Arbitrary)] +enum UsePrj1 { + #[deny(dead_code)] + V0(<(!, usize, !) as Fun>::Prj) +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/units.rs b/proptest-derive/tests/units.rs new file mode 100644 index 00000000..b3f7bcc8 --- /dev/null +++ b/proptest-derive/tests/units.rs @@ -0,0 +1,42 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; +extern crate proptest; +use proptest::prelude::Arbitrary; + +#[derive(Debug, Arbitrary)] +struct T0; + +#[derive(Debug, Arbitrary)] +struct T1 {} + +#[derive(Debug, Arbitrary)] +struct T2(); + +#[derive(Debug, Arbitrary)] +enum T3 { V0, } + +#[derive(Debug, Arbitrary)] +enum T4 { V1(), } + +#[derive(Debug, Arbitrary)] +enum T5 { V2 {}, } + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/value.rs b/proptest-derive/tests/value.rs new file mode 100644 index 00000000..7c30d4b1 --- /dev/null +++ b/proptest-derive/tests/value.rs @@ -0,0 +1,164 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::Arbitrary; + +#[derive(Debug, Arbitrary)] +struct T0 { + #[proptest(value = "42")] + field: usize, + #[proptest(value("24"))] + bar: usize, + #[proptest(value = "24 + 24usize")] + baz: usize, + #[proptest(value = 1337)] + quux: usize, + #[proptest(value(7331))] + wibble: usize, + #[proptest(value("3 * 2 + 3usize / 3"))] + wobble: usize, +} + +#[derive(Debug, Arbitrary)] +struct T1( + #[proptest(value = "24")] + usize, +); + +#[derive(Debug, Arbitrary)] +enum T2 { + V0, + #[proptest(value = "T2::V1 { field: 1337 }")] + V1 { + field: usize, + }, +} + +#[derive(Debug, Arbitrary)] +enum T3 { + V0, + #[proptest(value = "T3::V1(7331)")] + V1(usize), +} + +#[derive(Debug, Arbitrary)] +enum T4 { + V0, + V1 { + #[proptest(value = "6")] + field: usize, + }, +} + +#[derive(Debug, Arbitrary)] +enum T5 { + V0, + V1( + #[proptest(value = "9")] + usize + ), +} + +#[derive(Debug, Arbitrary)] +struct T6 { + #[proptest(value = "\"alpha\".to_string()")] + alpha: String, + #[proptest(strategy = "0..100usize")] + beta: usize, +} + + +fn foo() -> usize { + 42 +} + +#[derive(Debug, Arbitrary)] +struct CallFun { + #[proptest(value = "foo()")] + foo: usize, + + #[proptest(value(foo))] + bar: usize, +} + +proptest! { + #[test] + fn t0_fixed_fields(v: T0) { + prop_assert_eq!(v.field, 42); + prop_assert_eq!(v.bar, 24); + prop_assert_eq!(v.baz, 48); + prop_assert_eq!(v.quux, 1337); + prop_assert_eq!(v.wibble, 7331); + prop_assert_eq!(v.wobble, 7); + } + + #[test] + fn t1_field_always_24(v: T1) { + prop_assert_eq!(v.0, 24); + } + + #[test] + fn t2_v1_always_1337(v: T2) { + if let T2::V1 { field } = v { + prop_assert_eq!(field, 1337); + } + } + + #[test] + fn t3_v1_always_7331(v: T3) { + if let T3::V1(v) = v { + prop_assert_eq!(v, 7331); + } + } + + #[test] + fn t4_v1_always_1337(v: T4) { + if let T4::V1 { field } = v { + prop_assert_eq!(field, 6); + } + } + + #[test] + fn t5_v1_always_7331(v: T5) { + if let T5::V1(v) = v { + prop_assert_eq!(v, 9); + } + } + + #[test] + fn t6_alpha_beta(v: T6) { + prop_assert_eq!(v.alpha, "alpha".to_string()); + prop_assert!(v.beta < 100); + } + + #[test] + fn call_fun_always_42(v: CallFun) { + assert_eq!(v.foo, 42); + assert_eq!(v.bar, 42); + } +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} diff --git a/proptest-derive/tests/value_param.rs b/proptest-derive/tests/value_param.rs new file mode 100755 index 00000000..483defc7 --- /dev/null +++ b/proptest-derive/tests/value_param.rs @@ -0,0 +1,113 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_use] +extern crate proptest_derive; +#[macro_use] +extern crate proptest; +use proptest::prelude::*; + +#[derive(Debug, Arbitrary)] +enum T0 { + #[proptest(params = "u8", value = "T0::V0(params / 2)")] + V0(u8), +} + +#[derive(Debug, Arbitrary)] +enum T1 { + #[proptest(params = "u8", value = "T1::V0 { field: params * 2 }")] + V0 { + field: u8 + }, +} + +#[derive(Debug, Arbitrary)] +enum T2 { + V0( + #[proptest(params = "u8", value = "params.is_power_of_two()")] + bool + ), +} + +#[derive(Debug, Arbitrary)] +enum T3 { + V0 { + #[proptest(params = "u8", value = "params * params")] + field: u8 + }, +} + +#[derive(Debug, Arbitrary)] +struct T4 { + #[proptest(params = "u8", value = "params - 3")] + field: u8 +} + +fn add(x: u8) -> u8 { + x + 1 +} + +#[derive(Debug, Arbitrary)] +struct T5( + #[proptest(params = "u8", value = "add(params)")] + u8 +); + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +} + +proptest! { + #[test] + fn t0_test(v in any_with::(4)) { + let T0::V0(x) = v; + assert_eq!(x, 2); + } + + #[test] + fn t1_test(v in any_with::(4)) { + let T1::V0 { field: x } = v; + assert_eq!(x, 8); + } + + #[test] + fn t2_test_true(v in any_with::(4)) { + let T2::V0(x) = v; + assert!(x); + } + + #[test] + fn t2_test_false(v in any_with::(10)) { + let T2::V0(x) = v; + assert!(!x); + } + + #[test] + fn t3_test(v in any_with::(4)) { + let T3::V0 { field: x } = v; + assert_eq!(x, 16); + } + + #[test] + fn t4_test(v in any_with::(4)) { + assert_eq!(v.field, 1); + } + + #[test] + fn t5_test(v in any_with::(4)) { + assert_eq!(v.0, 5); + } +} \ No newline at end of file diff --git a/proptest-derive/tests/weight.rs b/proptest-derive/tests/weight.rs new file mode 100644 index 00000000..29bc8def --- /dev/null +++ b/proptest-derive/tests/weight.rs @@ -0,0 +1,52 @@ +// Copyright 2018 The proptest developers +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] + +#[macro_use] +extern crate proptest_derive; +extern crate proptest; +use proptest::prelude::Arbitrary; + +#[derive(Debug, Arbitrary)] +enum T1 { + #[proptest(weight = "3")] + V1, + V2, +} + +#[derive(Debug, Arbitrary)] +enum T2 { + V1, + #[proptest(weight("3"))] + V2, +} + +#[derive(Debug, Arbitrary)] +enum T3 { + #[proptest(weight(3))] + V1, + V2, +} + +#[derive(Debug, Arbitrary)] +enum T4 { + V1, + #[proptest(weight = 3)] + V2, +} + +#[test] +fn asserting_arbitrary() { + fn assert_arbitrary() {} + + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); + assert_arbitrary::(); +}