1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
// Copyright (c) The propfuzz Contributors
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Procedural macros for propfuzz tests.
//!
//! This crate is an implementation detail of `propfuzz` and is **not** meant to be used directly.
//! Use it through [`propfuzz`](https://crates.io/crates/propfuzz) instead.

extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{parse_macro_input, AttributeArgs, ItemFn};

mod config;
mod errors;
mod propfuzz_impl;

/// The core macro, used to annotate test methods.
///
/// Annotate a function with this in order to have it run as a property-based test using the
/// [`proptest`](https://docs.rs/proptest) framework. In the future, such tests will also be
/// available as fuzz targets.
///
/// # Examples
///
/// ```
/// // The prelude imports the `propfuzz` macro.
///
/// use propfuzz::prelude::*;
/// use proptest::collection::vec;
///
/// /// Reversing a list twice produces the same result.
/// #[propfuzz]
/// fn reverse(
///     #[propfuzz(strategy = "vec(any::<u32>(), 0..64)")]
///     mut list: Vec<u32>,
/// ) {
///     let list2 = list.clone();
///     list.reverse();
///     list.reverse();
///     prop_assert_eq!(list, list2);
/// }
/// ```
///
/// # Arguments
///
/// `propfuzz` supports a number of arguments which can be used to customize test behavior.
///
/// Attributes can be broken up with commas and split up across multiple lines like so:
///
/// ```
/// use propfuzz::prelude::*;
/// use proptest::collection::vec;
///
/// /// Options broken up across multiple lines.
/// #[propfuzz(cases = 1024, max_local_rejects = 10000)]
/// #[propfuzz(fork = true)]
/// fn reverse(
///     #[propfuzz(strategy = "vec(any::<u32>(), 0..64)")]
///     mut list: Vec<u32>,
/// ) {
///     let list2 = list.clone();
///     list.reverse();
///     list.reverse();
///     prop_assert_eq!(list, list2);
/// }
/// ```
///
/// ## Fuzzing configuration
///
/// These arguments are currently unused but may be set. They will be used in the future once fuzzing support is
/// available.
///
/// * `fuzz_default`: whether to fuzz this target by default. Defaults to `false`.
///
/// ## Proptest configuration
///
/// The following `proptest`
/// [configuration options](https://docs.rs/proptest/0.10/proptest/test_runner/struct.Config.html)
/// are supported:
///
/// * `cases`
/// * `max_local_rejects`
/// * `max_global_rejects`
/// * `max_flat_map_regens`
/// * `fork`
/// * `timeout`
/// * `max_shrink_time`
/// * `max_shrink_iters`
/// * `verbose`
///
/// ## Argument configuration
///
/// The following configuration options are supported on individual arguments:
///
/// * `strategy`: A strategy to generate and shrink values of the given type. The value must be a
///   string that parses as a Rust expression which evaluates to an implementation of
///   [`Strategy`](https://docs.rs/proptest/0.10/proptest/strategy/trait.Strategy.html)
///   for the given type. Defaults to [the
///   canonical strategy](https://docs.rs/proptest/0.10/proptest/arbitrary/trait.Arbitrary.html)
///   for the type.
#[proc_macro_attribute]
pub fn propfuzz(attr: TokenStream, item: TokenStream) -> TokenStream {
    let attr = parse_macro_input!(attr as AttributeArgs);
    let item = parse_macro_input!(item as ItemFn);

    propfuzz_impl::propfuzz_impl(attr, item)
        .unwrap_or_else(|err| err)
        .into()
}