1use std::fmt::Display;
12
13use proc_macro2::TokenStream;
14use quote::ToTokens;
15
16use crate::attr::ParsedAttributes;
17use syn;
18
19pub const STRUCT: &str = "struct";
25
26pub const STRUCT_FIELD: &str = "struct field";
28
29pub const ENUM: &str = "enum";
31
32pub const ENUM_VARIANT: &str = "enum variant";
34
35pub const ENUM_VARIANT_FIELD: &str = "enum variant field";
37
38pub const TY_VAR: &str = "a type variable";
40
41pub fn if_has_lifetimes(ctx: Ctx, ast: &syn::DeriveInput) {
47 if ast.generics.lifetimes().count() > 0 {
48 has_lifetimes(ctx);
49 }
50}
51
52pub fn if_anything_specified(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
54 if_enum_attrs_present(ctx, attrs, item);
55 if_strategy_present(ctx, attrs, item);
56 if_specified_params(ctx, attrs, item);
57 if_specified_filter(ctx, attrs, item);
58}
59
60pub fn if_enum_attrs_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
63 if_skip_present(ctx, attrs, item);
64 if_weight_present(ctx, attrs, item);
65}
66
67pub fn if_specified_filter(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
69 if !attrs.filter.is_empty() {
70 meaningless_filter(ctx, item);
71 }
72}
73
74pub fn if_specified_params(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
76 if attrs.params.is_set() {
77 parent_has_param(ctx, item);
78 }
79}
80
81pub fn if_strategy_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
83 use crate::attr::StratMode::*;
84 match attrs.strategy {
85 Arbitrary => {}
86 Strategy(_) => illegal_strategy(ctx, "strategy", item),
87 Value(_) => illegal_strategy(ctx, "value", item),
88 Regex(_) => illegal_regex(ctx, item),
89 }
90}
91
92pub fn if_present_on_unit_variant(ctx: Ctx, attrs: &ParsedAttributes) {
94 use crate::attr::StratMode::*;
96 match attrs.strategy {
97 Arbitrary => {}
98 Strategy(_) => strategy_on_unit_variant(ctx, "strategy"),
99 Value(_) => strategy_on_unit_variant(ctx, "value"),
100 Regex(_) => regex_on_unit_variant(ctx),
101 }
102
103 if attrs.params.is_set() {
104 params_on_unit_variant(ctx)
105 }
106
107 if !attrs.filter.is_empty() {
108 filter_on_unit_variant(ctx)
109 }
110}
111
112pub fn if_present_on_unit_struct(ctx: Ctx, attrs: &ParsedAttributes) {
114 if attrs.params.is_set() {
115 params_on_unit_struct(ctx)
116 }
117
118 if !attrs.filter.is_empty() {
119 filter_on_unit_struct(ctx)
120 }
121}
122
123pub fn if_skip_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
125 if attrs.skip {
126 illegal_skip(ctx, item)
127 }
128}
129
130pub fn if_weight_present(ctx: Ctx, attrs: &ParsedAttributes, item: &str) {
132 if attrs.weight.is_some() {
133 illegal_weight(ctx, item)
134 }
135}
136
137#[derive(Debug)]
146pub struct Fatal;
147
148pub type DeriveResult<T> = Result<T, Fatal>;
150
151pub type Ctx<'ctx> = &'ctx mut Context;
154
155#[derive(Default)]
159pub struct Context {
160 errors: Vec<String>,
161}
162
163impl Context {
164 pub fn error<T: Display>(&mut self, msg: T) {
166 self.errors.push(msg.to_string());
167 }
168
169 pub fn fatal<T: Display, A>(&mut self, msg: T) -> DeriveResult<A> {
172 self.error(msg);
173 Err(Fatal)
174 }
175
176 pub fn check(mut self) -> Result<(), TokenStream> {
180 fn compile_error(msg: &str) -> TokenStream {
181 quote! {
182 compile_error!(#msg);
183 }
184 }
185
186 match self.errors.len() {
187 0 => Ok(()),
188 1 => Err(compile_error(&self.errors.pop().unwrap())),
189 n => {
190 let mut msg = format!("{} errors:", n);
191 for err in self.errors {
192 msg.push_str("\n\t# ");
193 msg.push_str(&err);
194 }
195 Err(compile_error(&msg))
196 }
197 }
198 }
199}
200
201macro_rules! mk_err_msg {
208 ($code: ident, $msg: expr) => {
209 format!(
210 "[proptest_derive, {}] during #[derive(Arbitrary)]:\n{} Please see: https://proptest-rs.github.io/proptest/proptest-derive/errors.html#{} for more information.",
211 stringify!($code),
212 $msg,
213 (stringify!($code)).to_lowercase()
214 )
215 };
216}
217
218macro_rules! fatal {
220 ($error: ident, $code: ident, $msg: expr) => {
221 pub fn $error<T>(ctx: Ctx) -> DeriveResult<T> {
222 ctx.fatal(mk_err_msg!($code, $msg))
223 }
224 };
225 ($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident,
226 $msg: expr, $($fmt: tt)+) => {
227 pub fn $error<T>(ctx: Ctx, $($arg: $arg_ty),*) -> DeriveResult<T> {
228 ctx.fatal(mk_err_msg!($code, format!($msg, $($fmt)+)))
229 }
230 };
231}
232
233macro_rules! error {
235 ($error: ident, $code: ident, $msg: expr) => {
236 pub fn $error(ctx: Ctx) {
237 ctx.error(mk_err_msg!($code, $msg))
238 }
239 };
240 ($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident,
241 $msg: expr, $($fmt: tt)+) => {
242 pub fn $error(ctx: Ctx, $($arg: $arg_ty),*) {
243 ctx.error(mk_err_msg!($code, format!($msg, $($fmt)+)))
244 }
245 };
246}
247
248error!(
252 has_lifetimes,
253 E0001,
254 "Cannot derive `Arbitrary` for types with generic lifetimes, such as: \
255 `struct Foo<'a> { bar: &'a str }`. Currently, strategies for such types \
256 are impossible to define."
257);
258
259fatal!(
263 not_struct_or_enum,
264 E0002,
265 "Deriving is only possible for structs and enums. \
266 It is currently not defined unions."
267);
268
269error!(
272 uninhabited_struct,
273 E0003,
274 "The struct you are deriving `Arbitrary` for is uninhabited since one of \
275 its fields is uninhabited. An uninhabited type is by definition impossible \
276 to generate."
277);
278
279fatal!(
283 uninhabited_enum_with_no_variants,
284 E0004,
285 "The enum you are deriving `Arbitrary` for is uninhabited since it has no \
286 variants. An example of such an `enum` is: `enum Void {}`. \
287 An uninhabited type is by definition impossible to generate."
288);
289
290fatal!(
295 uninhabited_enum_variants_uninhabited,
296 E0005,
297 "The enum you are deriving `Arbitrary` for is uninhabited since all its \
298 variants are uninhabited. \
299 An uninhabited type is by definition impossible to generate."
300);
301
302error!(
306 uninhabited_enum_because_of_skipped_variants,
307 E0006,
308 "The enum you are deriving `Arbitrary` for is uninhabited for all intents \
309 and purposes since you have `#[proptest(skip)]`ed all inhabited variants. \
310 An uninhabited type is by definition impossible to generate."
311);
312
313error!(illegal_strategy(attr: &str, item: &str), E0007,
318 "`#[proptest({0} = \"<expr>\")]` is not allowed on {1}. Only struct fields, \
319 enum variants and fields inside those can use an explicit {0}.",
320 attr, item);
321
322error!(
326 illegal_regex(item: &str),
327 E0007,
328 "`#[proptest(regex = \"<string>\")]` is not allowed on {0}. Only struct \
329 fields, enum variant fields can use an explicit regex.",
330 item
331);
332
333error!(
336 illegal_skip(item: &str),
337 E0008,
338 "A {} can't be `#[proptest(skip)]`ed, only enum variants can be skipped.",
339 item
340);
341
342error!(
345 illegal_weight(item: &str),
346 E0009,
347 "`#[proptest(weight = <integer>)]` is not allowed on {} as it is \
348 meaningless. Only enum variants can be assigned weights.",
349 item
350);
351
352error!(
357 parent_has_param(item: &str),
358 E0010,
359 "Cannot set the associated type `Parameters` of `Arbitrary` with either \
360 `#[proptest(no_params)]` or `#[proptest(params(<type>)]` on {} since it \
361 was set on the parent.",
362 item
363);
364
365fatal!(
369 cant_set_param_but_not_strat(self_ty: &syn::Type, item: &str),
370 E0011,
371 "Cannot set `#[proptest(params = <type>)]` on {0} while not providing a \
372 strategy for the {0} to use it since `<{1} as Arbitrary<'a>>::Strategy` \
373 may require a different type than the one provided in `<type>`.",
374 item,
375 quote! { #self_ty }
376);
377
378error!(
383 meaningless_filter(item: &str),
384 E0012,
385 "Cannot set `#[proptest(filter = <expr>)]` on {} since it is set on the \
386 item which it is inside of that outer item specifies how to generate \
387 itself.",
388 item
389);
390
391error!(
394 inner_attr,
395 E0013, "Inner attributes `#![proptest(..)]` are not currently supported."
396);
397
398error!(
401 bare_proptest_attr,
402 E0014, "Bare `#[proptest]` attributes are not allowed."
403);
404
405error!(
408 literal_set_proptest,
409 E0015, "The attribute form `#[proptest = <literal>]` is not allowed."
410);
411
412error!(
415 immediate_literals,
416 E0016,
417 "Literals immediately inside `#[proptest(..)]` as in \
418 `#[proptest(<lit>, ..)]` are not allowed."
419);
420
421error!(
424 set_again(meta: &syn::Meta),
425 E0017,
426 "The attribute modifier `{}` inside `#[proptest(..)]` has already been \
427 set. To fix the error, please remove at least one such modifier.",
428 meta.path().into_token_stream()
429);
430
431error!(
434 did_you_mean(found: &str, expected: &str),
435 E0018,
436 "Unknown attribute modifier `{}` inside #[proptest(..)] is not allowed. \
437 Did you mean to use `{}` instead?",
438 found,
439 expected
440);
441
442error!(
445 unkown_modifier(modifier: &str),
446 E0018,
447 "Unknown attribute modifier `{}` inside `#[proptest(..)]` is not allowed.",
448 modifier
449);
450
451error!(
453 no_params_malformed,
454 E0019,
455 "The attribute modifier `no_params` inside `#[proptest(..)]` does not \
456 support any further configuration and must be a plain modifier as in \
457 `#[proptest(no_params)]`."
458);
459
460error!(
462 skip_malformed,
463 E0020,
464 "The attribute modifier `skip` inside `#[proptest(..)]` does not support \
465 any further configuration and must be a plain modifier as in \
466 `#[proptest(skip)]`."
467);
468
469error!(
471 weight_malformed(meta: &syn::Meta),
472 E0021,
473 "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
474 format `#[proptest({0} = <integer>)]` where `<integer>` is an integer that \
475 fits within a `u32`. An example: `#[proptest({0} = 2)]` to set a relative \
476 weight of 2.",
477 meta.path().into_token_stream()
478);
479
480fatal!(
484 overspecified_param,
485 E0022,
486 "Cannot set `#[proptest(no_params)]` as well as \
487 `#[proptest(params(<type>))]` simultaneously. \
488 Please pick one of these attributes."
489);
490
491error!(
497 param_malformed,
498 E0023,
499 "The attribute modifier `params` inside #[proptest(..)] must have the \
500 format `#[proptest(params = \"<type>\")]` where `<type>` is a valid type \
501 in Rust. An example: `#[proptest(params = \"ComplexType<Foo>\")]`."
502);
503
504fatal!(
509 overspecified_strat,
510 E0025,
511 "Cannot set more than one of `#[proptest(value = \"<expr>\")]`,
512 `#[proptest(strategy = \"<expr>\")]`, `#[proptest(regex = \"<string>\")]` \
513 simultaneously. Please pick one of these attributes."
514);
515
516error!(
521 strategy_malformed(meta: &syn::Meta),
522 E0026,
523 "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
524 format `#[proptest({0} = \"<expr>\")]` where `<expr>` is a valid Rust \
525 expression.",
526 meta.path().into_token_stream()
527);
528
529error!(
533 filter_malformed(meta: &syn::Meta),
534 E0027,
535 "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
536 format `#[proptest({0} = \"<expr>\")]` where `<expr>` is a valid Rust \
537 expression.",
538 meta.path().into_token_stream()
539);
540
541error!(
544 skipped_variant_has_weight(item: &str),
545 E0028,
546 "A variant has been skipped. Setting `#[proptest(weight = <value>)]` on \
547 the {} is meaningless and is not allowed.",
548 item
549);
550
551error!(
554 skipped_variant_has_param(item: &str),
555 E0028,
556 "A variant has been skipped. Setting `#[proptest(no_param)]` or \
557 `#[proptest(params(<type>))]` on the {} is meaningless and is not allowed.",
558 item
559);
560
561error!(
564 skipped_variant_has_strat(item: &str),
565 E0028,
566 "A variant has been skipped. Setting `#[proptest(value = \"<expr>\")]` or \
567 `#[proptest(strategy = \"<expr>\")]` on the {} is meaningless and is not \
568 allowed.",
569 item
570);
571
572error!(skipped_variant_has_filter(item: &str), E0028,
576 "A variant has been skipped. Setting `#[proptest(filter = \"<expr>\")]` or \
577 on the {} is meaningless and is not allowed.",
578 item);
579
580error!(
584 strategy_on_unit_variant(what: &str),
585 E0029,
586 "Setting `#[proptest({0} = \"<expr>\")]` on a unit variant has no effect \
587 and is redundant because there is nothing to configure.",
588 what
589);
590
591error!(regex_on_unit_variant, E0029,
593 "Setting `#[proptest(regex = \"<string>\")]` on a unit variant has no effect \
594 and is redundant because there is nothing to configure.");
595
596error!(
599 params_on_unit_variant,
600 E0029,
601 "Setting `#[proptest(params = \"<type>\")]` on a unit variant has \
602 no effect and is redundant because there is nothing to configure."
603);
604
605error!(
608 filter_on_unit_variant,
609 E0029,
610 "Setting `#[proptest(filter = \"<expr>\")]` on a unit variant has \
611 no effect and is redundant because there is nothing to further filter."
612);
613
614error!(params_on_unit_struct, E0030,
618 "Setting `#[proptest(params = \"<type>\")]` on a unit struct has no effect \
619 and is redundant because there is nothing to configure.");
620
621error!(filter_on_unit_struct, E0030,
625 "Setting `#[proptest(filter = \"<expr>\")]` on a unit struct has no effect \
626 and is redundant because there is nothing to filter.");
627
628error!(
631 no_bound_set_on_non_tyvar,
632 E0031,
633 "Setting `#[proptest(no_bound)]` on something that is not a type variable \
634 has no effect and is redundant. Therefore it is not allowed."
635);
636
637error!(
639 no_bound_malformed,
640 E0032,
641 "The attribute modifier `no_bound` inside `#[proptest(..)]` does not \
642 support any further configuration and must be a plain modifier as in \
643 `#[proptest(no_bound)]`."
644);
645
646error!(
648 weight_overflowing,
649 E0033,
650 "The sum of the weights specified on variants of the enum you are \
651 deriving `Arbitrary` for overflows an `u32` which it can't do."
652);
653
654error!(
657 regex_malformed,
658 E0034,
659 "The attribute modifier `regex` inside `#[proptest(..)]` must have the \
660 format `#[proptest(regex = \"<string>\")]` where `<string>` is a valid
661 regular expression embedded in a Rust string slice."
662);
663
664error!(
669 cant_set_param_and_regex(item: &str),
670 E0035,
671 "Cannot set #[proptest(regex = \"<string>\")] and \
672 `#[proptest(params = <type>)]` on {0} because the latter is a logic bug \
673 since `params` cannot be used in `<string>`.",
674 item
675);
676
677#[cfg(test)]
678mod tests {
679 #[test]
680 fn test_mk_err_msg_format() {
681 assert_eq!(
682 mk_err_msg!(E0001, "This is a sample error message."),
683 "[proptest_derive, E0001] during #[derive(Arbitrary)]:\nThis is a sample error message. Please see: https://proptest-rs.github.io/proptest/proptest-derive/errors.html#e0001 for more information."
684 );
685 }
686}