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 concat!(
210 "[proptest_derive, ",
211 stringify!($code),
212 "]",
213 " during #[derive(Arbitrary)]:\n",
214 $msg,
215 " Please see: https://PATH/TO/foo#",
216 stringify!($code),
217 " for more information."
218 )
219 };
220}
221
222macro_rules! fatal {
224 ($error: ident, $code: ident, $msg: expr) => {
225 pub fn $error<T>(ctx: Ctx) -> DeriveResult<T> {
226 ctx.fatal(mk_err_msg!($code, $msg))
227 }
228 };
229 ($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident,
230 $msg: expr, $($fmt: tt)+) => {
231 pub fn $error<T>(ctx: Ctx, $($arg: $arg_ty),*) -> DeriveResult<T> {
232 ctx.fatal(format!(mk_err_msg!($code, $msg), $($fmt)+))
233 }
234 };
235}
236
237macro_rules! error {
239 ($error: ident, $code: ident, $msg: expr) => {
240 pub fn $error(ctx: Ctx) {
241 ctx.error(mk_err_msg!($code, $msg))
242 }
243 };
244 ($error: ident ($($arg: ident: $arg_ty: ty),*), $code: ident,
245 $msg: expr, $($fmt: tt)+) => {
246 pub fn $error(ctx: Ctx, $($arg: $arg_ty),*) {
247 ctx.error(format!(mk_err_msg!($code, $msg), $($fmt)+))
248 }
249 };
250}
251
252error!(
256 has_lifetimes,
257 E0001,
258 "Cannot derive `Arbitrary` for types with generic lifetimes, such as: \
259 `struct Foo<'a> { bar: &'a str }`. Currently, strategies for such types \
260 are impossible to define."
261);
262
263fatal!(
267 not_struct_or_enum,
268 E0002,
269 "Deriving is only possible for structs and enums. \
270 It is currently not defined unions."
271);
272
273error!(
276 uninhabited_struct,
277 E0003,
278 "The struct you are deriving `Arbitrary` for is uninhabited since one of \
279 its fields is uninhabited. An uninhabited type is by definition impossible \
280 to generate."
281);
282
283fatal!(
287 uninhabited_enum_with_no_variants,
288 E0004,
289 "The enum you are deriving `Arbitrary` for is uninhabited since it has no \
290 variants. An example of such an `enum` is: `enum Void {}`. \
291 An uninhabited type is by definition impossible to generate."
292);
293
294fatal!(
299 uninhabited_enum_variants_uninhabited,
300 E0005,
301 "The enum you are deriving `Arbitrary` for is uninhabited since all its \
302 variants are uninhabited. \
303 An uninhabited type is by definition impossible to generate."
304);
305
306error!(
310 uninhabited_enum_because_of_skipped_variants,
311 E0006,
312 "The enum you are deriving `Arbitrary` for is uninhabited for all intents \
313 and purposes since you have `#[proptest(skip)]`ed all inhabited variants. \
314 An uninhabited type is by definition impossible to generate."
315);
316
317error!(illegal_strategy(attr: &str, item: &str), E0007,
322 "`#[proptest({0} = \"<expr>\")]` is not allowed on {1}. Only struct fields, \
323 enum variants and fields inside those can use an explicit {0}.",
324 attr, item);
325
326error!(
330 illegal_regex(item: &str),
331 E0007,
332 "`#[proptest(regex = \"<string>\")]` is not allowed on {0}. Only struct \
333 fields, enum variant fields can use an explicit regex.",
334 item
335);
336
337error!(
340 illegal_skip(item: &str),
341 E0008,
342 "A {} can't be `#[proptest(skip)]`ed, only enum variants can be skipped.",
343 item
344);
345
346error!(
349 illegal_weight(item: &str),
350 E0009,
351 "`#[proptest(weight = <integer>)]` is not allowed on {} as it is \
352 meaningless. Only enum variants can be assigned weights.",
353 item
354);
355
356error!(
361 parent_has_param(item: &str),
362 E0010,
363 "Cannot set the associated type `Parameters` of `Arbitrary` with either \
364 `#[proptest(no_params)]` or `#[proptest(params(<type>)]` on {} since it \
365 was set on the parent.",
366 item
367);
368
369fatal!(
373 cant_set_param_but_not_strat(self_ty: &syn::Type, item: &str),
374 E0011,
375 "Cannot set `#[proptest(params = <type>)]` on {0} while not providing a \
376 strategy for the {0} to use it since `<{1} as Arbitrary<'a>>::Strategy` \
377 may require a different type than the one provided in `<type>`.",
378 item,
379 quote! { #self_ty }
380);
381
382error!(
387 meaningless_filter(item: &str),
388 E0012,
389 "Cannot set `#[proptest(filter = <expr>)]` on {} since it is set on the \
390 item which it is inside of that outer item specifies how to generate \
391 itself.",
392 item
393);
394
395error!(
398 inner_attr,
399 E0013, "Inner attributes `#![proptest(..)]` are not currently supported."
400);
401
402error!(
405 bare_proptest_attr,
406 E0014, "Bare `#[proptest]` attributes are not allowed."
407);
408
409error!(
412 literal_set_proptest,
413 E0015, "The attribute form `#[proptest = <literal>]` is not allowed."
414);
415
416error!(
419 immediate_literals,
420 E0016,
421 "Literals immediately inside `#[proptest(..)]` as in \
422 `#[proptest(<lit>, ..)]` are not allowed."
423);
424
425error!(
428 set_again(meta: &syn::Meta),
429 E0017,
430 "The attribute modifier `{}` inside `#[proptest(..)]` has already been \
431 set. To fix the error, please remove at least one such modifier.",
432 meta.path().into_token_stream()
433);
434
435error!(
438 did_you_mean(found: &str, expected: &str),
439 E0018,
440 "Unknown attribute modifier `{}` inside #[proptest(..)] is not allowed. \
441 Did you mean to use `{}` instead?",
442 found,
443 expected
444);
445
446error!(
449 unkown_modifier(modifier: &str),
450 E0018,
451 "Unknown attribute modifier `{}` inside `#[proptest(..)]` is not allowed.",
452 modifier
453);
454
455error!(
457 no_params_malformed,
458 E0019,
459 "The attribute modifier `no_params` inside `#[proptest(..)]` does not \
460 support any further configuration and must be a plain modifier as in \
461 `#[proptest(no_params)]`."
462);
463
464error!(
466 skip_malformed,
467 E0020,
468 "The attribute modifier `skip` inside `#[proptest(..)]` does not support \
469 any further configuration and must be a plain modifier as in \
470 `#[proptest(skip)]`."
471);
472
473error!(
475 weight_malformed(meta: &syn::Meta),
476 E0021,
477 "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
478 format `#[proptest({0} = <integer>)]` where `<integer>` is an integer that \
479 fits within a `u32`. An example: `#[proptest({0} = 2)]` to set a relative \
480 weight of 2.",
481 meta.path().into_token_stream()
482);
483
484fatal!(
488 overspecified_param,
489 E0022,
490 "Cannot set `#[proptest(no_params)]` as well as \
491 `#[proptest(params(<type>))]` simultaneously. \
492 Please pick one of these attributes."
493);
494
495error!(
501 param_malformed,
502 E0023,
503 "The attribute modifier `params` inside #[proptest(..)] must have the \
504 format `#[proptest(params = \"<type>\")]` where `<type>` is a valid type \
505 in Rust. An example: `#[proptest(params = \"ComplexType<Foo>\")]`."
506);
507
508fatal!(
513 overspecified_strat,
514 E0025,
515 "Cannot set more than one of `#[proptest(value = \"<expr>\")]`,
516 `#[proptest(strategy = \"<expr>\")]`, `#[proptest(regex = \"<string>\")]` \
517 simultaneously. Please pick one of these attributes."
518);
519
520error!(
525 strategy_malformed(meta: &syn::Meta),
526 E0026,
527 "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
528 format `#[proptest({0} = \"<expr>\")]` where `<expr>` is a valid Rust \
529 expression.",
530 meta.path().into_token_stream()
531);
532
533error!(
537 filter_malformed(meta: &syn::Meta),
538 E0027,
539 "The attribute modifier `{0}` inside `#[proptest(..)]` must have the \
540 format `#[proptest({0} = \"<expr>\")]` where `<expr>` is a valid Rust \
541 expression.",
542 meta.path().into_token_stream()
543);
544
545error!(
548 skipped_variant_has_weight(item: &str),
549 E0028,
550 "A variant has been skipped. Setting `#[proptest(weight = <value>)]` on \
551 the {} is meaningless and is not allowed.",
552 item
553);
554
555error!(
558 skipped_variant_has_param(item: &str),
559 E0028,
560 "A variant has been skipped. Setting `#[proptest(no_param)]` or \
561 `#[proptest(params(<type>))]` on the {} is meaningless and is not allowed.",
562 item
563);
564
565error!(
568 skipped_variant_has_strat(item: &str),
569 E0028,
570 "A variant has been skipped. Setting `#[proptest(value = \"<expr>\")]` or \
571 `#[proptest(strategy = \"<expr>\")]` on the {} is meaningless and is not \
572 allowed.",
573 item
574);
575
576error!(skipped_variant_has_filter(item: &str), E0028,
580 "A variant has been skipped. Setting `#[proptest(filter = \"<expr>\")]` or \
581 on the {} is meaningless and is not allowed.",
582 item);
583
584error!(
588 strategy_on_unit_variant(what: &str),
589 E0029,
590 "Setting `#[proptest({0} = \"<expr>\")]` on a unit variant has no effect \
591 and is redundant because there is nothing to configure.",
592 what
593);
594
595error!(regex_on_unit_variant, E0029,
597 "Setting `#[proptest(regex = \"<string>\")]` on a unit variant has no effect \
598 and is redundant because there is nothing to configure.");
599
600error!(
603 params_on_unit_variant,
604 E0029,
605 "Setting `#[proptest(params = \"<type>\")]` on a unit variant has \
606 no effect and is redundant because there is nothing to configure."
607);
608
609error!(
612 filter_on_unit_variant,
613 E0029,
614 "Setting `#[proptest(filter = \"<expr>\")]` on a unit variant has \
615 no effect and is redundant because there is nothing to further filter."
616);
617
618error!(params_on_unit_struct, E0030,
622 "Setting `#[proptest(params = \"<type>\")]` on a unit struct has no effect \
623 and is redundant because there is nothing to configure.");
624
625error!(filter_on_unit_struct, E0030,
629 "Setting `#[proptest(filter = \"<expr>\")]` on a unit struct has no effect \
630 and is redundant because there is nothing to filter.");
631
632error!(
635 no_bound_set_on_non_tyvar,
636 E0031,
637 "Setting `#[proptest(no_bound)]` on something that is not a type variable \
638 has no effect and is redundant. Therefore it is not allowed."
639);
640
641error!(
643 no_bound_malformed,
644 E0032,
645 "The attribute modifier `no_bound` inside `#[proptest(..)]` does not \
646 support any further configuration and must be a plain modifier as in \
647 `#[proptest(no_bound)]`."
648);
649
650error!(
652 weight_overflowing,
653 E0033,
654 "The sum of the weights specified on variants of the enum you are \
655 deriving `Arbitrary` for overflows an `u32` which it can't do."
656);
657
658error!(
661 regex_malformed,
662 E0034,
663 "The attribute modifier `regex` inside `#[proptest(..)]` must have the \
664 format `#[proptest(regex = \"<string>\")]` where `<string>` is a valid
665 regular expression embedded in a Rust string slice."
666);
667
668error!(
673 cant_set_param_and_regex(item: &str),
674 E0035,
675 "Cannot set #[proptest(regex = \"<string>\")] and \
676 `#[proptest(params = <type>)]` on {0} because the latter is a logic bug \
677 since `params` cannot be used in `<string>`.",
678 item
679);