| // Copyright 2018 The proptest developers |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| //! Provides error messages and some checkers. |
| |
| use std::fmt::Display; |
| |
| use proc_macro2::TokenStream; |
| |
| use syn; |
| use crate::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() { meaningless_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 crate::attr::StratMode::*; |
| match attrs.strategy { |
| Arbitrary => {}, |
| Strategy(_) => illegal_strategy(ctx, "strategy", item), |
| Value(_) => illegal_strategy(ctx, "value", item), |
| Regex(_) => illegal_regex(ctx, 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 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) |
| } |
| } |
| |
| /// 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 |
| //============================================================================== |
| |
| /// Denotes that a fatal error happened in dealing somewhere in the |
| /// procedural macro pipeline. A fatal error is different from a |
| /// normal error in the sense that it halts progress in the macro |
| /// immediately instead of allowing other errors to be accumulated. |
| #[derive(Debug)] |
| pub struct Fatal; |
| |
| /// The return type of a possibly fatal computation in the macro. |
| pub type DeriveResult<T> = Result<T, Fatal>; |
| |
| /// A mutable view / shorthand for the context. |
| /// Prefer this type over `Context` in functions. |
| pub type Ctx<'ctx> = &'ctx mut Context; |
| |
| /// The context / environment that the macro is operating in. |
| /// Right now, it simply tracks all the errors collected during |
| /// the running of the macro. |
| #[derive(Default)] |
| pub struct Context { |
| errors: Vec<String>, |
| } |
| |
| impl Context { |
| /// Add a non-fatal error to the context. |
| pub fn error<T: Display>(&mut self, msg: T) { |
| self.errors.push(msg.to_string()); |
| } |
| |
| /// Add an error to the context and procuce and produce an erroring |
| /// computation that will halt the macro. |
| pub fn fatal<T: Display, A>(&mut self, msg: T) -> DeriveResult<A> { |
| self.error(msg); |
| Err(Fatal) |
| } |
| |
| /// Consume the context and if there were any errors, |
| /// emit `compile_error!(..)` such that the crate using |
| /// `#[derive(Arbitrary)]` will fail to compile. |
| 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 |
| //============================================================================== |
| |
| /// Produce an error string with the error `$code` which corresponds |
| /// to the given `$message`. |
| 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 halt compilation immediately. |
| 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)+)) |
| } |
| }; |
| } |
| |
| /// A macro constructing fatal errors that do not 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, |
| "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."); |
| |
| // 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 = "<expr>")]` or |
| // `#[proptest(value = "<expr>")]` 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} = \"<expr>\")]` is not allowed on {1}. Only struct fields, \ |
| enum variants and fields inside those can use an explicit {0}.", |
| attr, item); |
| |
| // Happens when `#[proptest(regex = "<string>")]` is specified on an `item` |
| // that does not support setting an explicit value or strategy. |
| // See `illegal_strategy` for more. |
| 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); |
| |
| // 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 = <integer>)]` is specified on an |
| // `item` that does not support weighting. |
| 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); |
| |
| // Happens when `#[proptest(params = <type>)]` 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, |
| "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); |
| |
| // Happens when `#[proptest(params = <type>)]` is set on `item` |
| // but not `#[proptest(strategy = <expr>)]`. |
| // This does not apply to the top level type declaration. |
| 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 }); |
| |
| // Happens when `#[proptest(filter = "<expr>")]` is set on `item`, |
| // but the parent of the `item` explicitly specifies a value or strategy, |
| // which would cause the value to be generated without consulting the |
| // `filter`. |
| 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); |
| |
| // 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 = <literal>)]` is used. |
| // Only the form `#[proptest(<contents>)]` is supported. |
| error!(literal_set_proptest, E0015, |
| "The attribute form `#[proptest = <literal>]` is not allowed."); |
| |
| // Happens when `<modifier>` in `#[proptest(<modifier>)]` is a literal and |
| // not a real modifier. |
| error!(immediate_literals, E0016, |
| "Literals immediately inside `#[proptest(..)]` as in \ |
| `#[proptest(<lit>, ..)]` are not allowed."); |
| |
| // Happens when `<modifier>` in `#[proptest(<modifier>)]` 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 `<modifier>` in `#[proptest(<modifier>)]` 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 `<modifier>` in `#[proptest(<modifier>)]` 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} = <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()); |
| |
| // Happens when both `#[proptest(params = "<type>")]` and |
| // `#[proptest(no_params)]` were specified. They are mutually |
| // exclusive choices. The user can resolve this by picking one. |
| fatal!(overspecified_param, E0022, |
| "Cannot set `#[proptest(no_params)]` as well as \ |
| `#[proptest(params(<type>))]` simultaneously. \ |
| Please pick one of these attributes."); |
| |
| // This happens when `#[proptest(params..)]` is malformed. |
| // For example, `#[proptest(params)]` is malformed. Another example is when |
| // `<type>` inside `#[proptest(params = "<type>")]` or |
| // `#[proptest(params("<type>"))]` is malformed. In other words, `<type>` 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 = \"<type>\")]` where `<type>` is a valid type \ |
| in Rust. An example: `#[proptest(params = \"ComplexType<Foo>\")]`."); |
| |
| // Happens when syn can't interpret <tts> in `#[proptest <tts>]`. |
| error!(no_interp_meta, E0024, |
| "The tokens `<tts>` in #[proptest <tts>] do not make for a valid attribute."); |
| |
| // Happens when more than one of `#[proptest(strategy..)]`, |
| // `#[proptest(value..)]`, or `#[proptest(regex..)]` were specified. |
| // They are mutually exclusive choices. |
| // The user can resolve this by picking one. |
| 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."); |
| |
| // Happens when `#[proptest(strategy..)]` or `#[proptest(value..)]` is |
| // malformed. For example, `<expr>` inside `#[proptest(strategy = "<expr>")]` |
| // or `#[proptest(value = "<expr>")]` is malformed. In other words, `<expr>` |
| // 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} = \"<expr>\")]` where `<expr>` is a valid Rust \ |
| expression.", |
| meta.name()); |
| |
| // Happens when `#[proptest(filter..)]` is malformed. |
| // For example, `<expr>` inside `#[proptest(filter = "<expr>")]` or |
| // is malformed. In other words, `<expr>` 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} = \"<expr>\")]` where `<expr>` 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 = <value>)]` 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(<type>))]` 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 = \"<expr>\")]` or \ |
| `#[proptest(strategy = \"<expr>\")]` 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 = \"<expr>\")]` 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 = "<expr>")]` or `#[proptest(value = "<expr>")]` |
| // would be pointless. |
| 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); |
| |
| // See `strategy_on_unit_variant`. |
| 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."); |
| |
| // There's only one way to produce a specific unit variant, so setting |
| // `#[proptest(params = "<type>")]` would be pointless. |
| 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."); |
| |
| // There's only one way to produce a specific unit variant, so setting |
| // `#[proptest(filter = "<expr>")]` would be pointless. |
| 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."); |
| |
| // Occurs when `#[proptest(params = "<type>")]` 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 = \"<type>\")]` on a unit struct has no effect \ |
| and is redundant because there is nothing to configure."); |
| |
| // Occurs when `#[proptest(filter = "<expr>")]` 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 = \"<expr>\")]` 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."); |
| |
| // Happens when `#[proptest(regex..)]` is malformed. |
| // For example, `#[proptest(regex = 1)]` is not a valid form. |
| 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."); |
| |
| // Happens when `#[proptest(params = <type>)]` is set on `item` and then |
| // `#[proptest(regex = "<string>")]` is also set. We reject this because |
| // the params can't be used. TODO: reduce this to a warning once we can |
| // emit warnings. |
| 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); |