use std::fmt::Display;
use proc_macro2::TokenStream;
use crate::attr::ParsedAttributes;
use syn;
pub const STRUCT: &str = "struct";
pub const STRUCT_FIELD: &str = "struct field";
pub const ENUM: &str = "enum";
pub const ENUM_VARIANT: &str = "enum variant";
pub const ENUM_VARIANT_FIELD: &str = "enum variant field";
pub const TY_VAR: &str = "a type variable";
pub fn if_has_lifetimes(ctx: Ctx, ast: &syn::DeriveInput) {
if ast.generics.lifetimes().count() > 0 {
has_lifetimes(ctx);
}
}
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);
}
pub fn if_enum_attrs_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
if_skip_present(ctx, attrs, item);
if_weight_present(ctx, attrs, item);
}
pub fn if_specified_filter(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
if !attrs.filter.is_empty() {
meaningless_filter(ctx, item);
}
}
pub fn if_specified_params(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
if attrs.params.is_set() {
parent_has_param(ctx, item);
}
}
pub fn if_strategy_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
use crate::attr::StratMode::*;
match attrs.strategy {
Arbitrary => {}
Strategy(_) => illegal_strategy(ctx, "strategy", item),
Value(_) => illegal_strategy(ctx, "value", item),
Regex(_) => illegal_regex(ctx, item),
}
}
pub fn if_present_on_unit_variant(ctx: Ctx, attrs: &ParsedAttributes) {
use crate::attr::StratMode::*;
match attrs.strategy {
Arbitrary => {}
Strategy(_) => strategy_on_unit_variant(ctx, "strategy"),
Value(_) => strategy_on_unit_variant(ctx, "value"),
Regex(_) => regex_on_unit_variant(ctx),
}
if attrs.params.is_set() {
params_on_unit_variant(ctx)
}
if !attrs.filter.is_empty() {
filter_on_unit_variant(ctx)
}
}
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)
}
}
pub fn if_skip_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
if attrs.skip {
illegal_skip(ctx, item)
}
}
pub fn if_weight_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
if attrs.weight.is_some() {
illegal_weight(ctx, item)
}
}
#[derive(Debug)]
pub struct Fatal;
pub type DeriveResult<T> = Result<T, Fatal>;
pub type Ctx<'ctx> = &'ctx mut Context;
#[derive(Default)]
pub struct Context {
errors: Vec<String>,
}
impl Context {
pub fn error<T: Display>(&mut self, msg: T) {
self.errors.push(msg.to_string());
}
pub fn fatal<T: Display, A>(&mut self, msg: T) -> DeriveResult<A> {
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))
}
}
}
}
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."
)
};
}
macro_rules! fatal {
($error: ident, $code: ident, $msg: expr) => {
pub fn $error<T>(ctx: Ctx) -> DeriveResult<T> {
ctx.fatal(mk_err_msg!($code, $msg))
}
};
($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident,
$msg: expr, $($fmt: tt)+) => {
pub fn $error<T>(ctx: Ctx, $($arg: $arg_ty),*) -> DeriveResult<T> {
ctx.fatal(format!(mk_err_msg!($code, $msg), $($fmt)+))
}
};
}
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)+))
}
};
}
error!(
has_lifetimes,
E0001,
"Cannot derive `Arbitrary` for types with generic lifetimes, such as: \
`struct Foo<'a> { bar: &'a str }`. Currently, strategies for such types \
are impossible to define."
);
fatal!(
not_struct_or_enum,
E0002,
"Deriving is only possible for structs and enums. \
It is currently not defined unions."
);
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."
);
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."
);
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."
);
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."
);
error!(illegal_strategy(attr: &str, item: &str), E0007,
"`#[proptest({0} = \"<expr>\")]` is not allowed on {1}. Only struct fields, \
enum variants and fields inside those can use an explicit {0}.",
attr, item);
error!(
illegal_regex(item: &str),
E0007,
"`#[proptest(regex = \"<string>\")]` is not allowed on {0}. Only struct \
fields, enum variant fields can use an explicit regex.",
item
);
error!(
illegal_skip(item: &str),
E0008,
"A {} can't be `#[proptest(skip)]`ed, only enum variants can be skipped.",
item
);
error!(
illegal_weight(item: &str),
E0009,
"`#[proptest(weight = <integer>)]` is not allowed on {} as it is \
meaningless. Only enum variants can be assigned weights.",
item
);
error!(
parent_has_param(item: &str),
E0010,
"Cannot set the associated type `Parameters` of `Arbitrary` with either \
`#[proptest(no_params)]` or `#[proptest(params(<type>)]` on {} since it \
was set on the parent.",
item
);
fatal!(
cant_set_param_but_not_strat(self_ty: &syn::Type, item: &str),
E0011,
"Cannot set `#[proptest(params = <type>)]` 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 `<type>`.",
item,
quote! { #self_ty }
);
error!(
meaningless_filter(item: &str),
E0012,
"Cannot set `#[proptest(filter = <expr>)]` on {} since it is set on the \
item which it is inside of that outer item specifies how to generate \
itself.",
item
);
error!(
inner_attr,
E0013, "Inner attributes `#![proptest(..)]` are not currently supported."
);
error!(
bare_proptest_attr,
E0014, "Bare `#[proptest]` attributes are not allowed."
);
error!(
literal_set_proptest,
E0015, "The attribute form `#[proptest = <literal>]` is not allowed."
);
error!(
immediate_literals,
E0016,
"Literals immediately inside `#[proptest(..)]` as in \
`#[proptest(<lit>, ..)]` are not allowed."
);
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()
);
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
);
error!(
unkown_modifier(modifier: &str),
E0018,
"Unknown attribute modifier `{}` inside `#[proptest(..)]` is not allowed.",
modifier
);
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)]`."
);
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)]`."
);
error!(
weight_malformed(meta: &syn::Meta),
E0021,
"The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
format `#[proptest({0} = <integer>)]` where `<integer>` is an integer that \
fits within a `u32`. An example: `#[proptest({0} = 2)]` to set a relative \
weight of 2.",
meta.name()
);
fatal!(
overspecified_param,
E0022,
"Cannot set `#[proptest(no_params)]` as well as \
`#[proptest(params(<type>))]` simultaneously. \
Please pick one of these attributes."
);
error!(
param_malformed,
E0023,
"The attribute modifier `params` inside #[proptest(..)] must have the \
format `#[proptest(params = \"<type>\")]` where `<type>` is a valid type \
in Rust. An example: `#[proptest(params = \"ComplexType<Foo>\")]`."
);
error!(no_interp_meta, E0024,
"The tokens `<tts>` in #[proptest <tts>] do not make for a valid attribute.");
fatal!(
overspecified_strat,
E0025,
"Cannot set more than one of `#[proptest(value = \"<expr>\")]`,
`#[proptest(strategy = \"<expr>\")]`, `#[proptest(regex = \"<string>\")]` \
simultaneously. Please pick one of these attributes."
);
error!(
strategy_malformed(meta: &syn::Meta),
E0026,
"The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
format `#[proptest({0} = \"<expr>\")]` where `<expr>` is a valid Rust \
expression.",
meta.name()
);
error!(
filter_malformed(meta: &syn::Meta),
E0027,
"The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
format `#[proptest({0} = \"<expr>\")]` where `<expr>` is a valid Rust \
expression.",
meta.name()
);
error!(
skipped_variant_has_weight(item: &str),
E0028,
"A variant has been skipped. Setting `#[proptest(weight = <value>)]` on \
the {} is meaningless and is not allowed.",
item
);
error!(
skipped_variant_has_param(item: &str),
E0028,
"A variant has been skipped. Setting `#[proptest(no_param)]` or \
`#[proptest(params(<type>))]` on the {} is meaningless and is not allowed.",
item
);
error!(
skipped_variant_has_strat(item: &str),
E0028,
"A variant has been skipped. Setting `#[proptest(value = \"<expr>\")]` or \
`#[proptest(strategy = \"<expr>\")]` on the {} is meaningless and is not \
allowed.",
item
);
error!(skipped_variant_has_filter(item: &str), E0028,
"A variant has been skipped. Setting `#[proptest(filter = \"<expr>\")]` or \
on the {} is meaningless and is not allowed.",
item);
error!(
strategy_on_unit_variant(what: &str),
E0029,
"Setting `#[proptest({0} = \"<expr>\")]` on a unit variant has no effect \
and is redundant because there is nothing to configure.",
what
);
error!(regex_on_unit_variant, E0029,
"Setting `#[proptest(regex = \"<string>\")]` on a unit variant has no effect \
and is redundant because there is nothing to configure.");
error!(
params_on_unit_variant,
E0029,
"Setting `#[proptest(params = \"<type>\")]` on a unit variant has \
no effect and is redundant because there is nothing to configure."
);
error!(
filter_on_unit_variant,
E0029,
"Setting `#[proptest(filter = \"<expr>\")]` on a unit variant has \
no effect and is redundant because there is nothing to further filter."
);
error!(params_on_unit_struct, E0030,
"Setting `#[proptest(params = \"<type>\")]` on a unit struct has no effect \
and is redundant because there is nothing to configure.");
error!(filter_on_unit_struct, E0030,
"Setting `#[proptest(filter = \"<expr>\")]` on a unit struct has no effect \
and is redundant because there is nothing to filter.");
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."
);
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)]`."
);
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."
);
error!(
regex_malformed,
E0034,
"The attribute modifier `regex` inside `#[proptest(..)]` must have the \
format `#[proptest(regex = \"<string>\")]` where `<string>` is a valid
regular expression embedded in a Rust string slice."
);
error!(
cant_set_param_and_regex(item: &str),
E0035,
"Cannot set #[proptest(regex = \"<string>\")] and \
`#[proptest(params = <type>)]` on {0} because the latter is a logic bug \
since `params` cannot be used in `<string>`.",
item
);