proptest_derive/
util.rs

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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.

//! Mostly useful utilities for syn used in the crate.

use std::borrow::Borrow;

use syn;

//==============================================================================
// General AST manipulation and types
//==============================================================================

/// Extract the list of fields from a `Fields` from syn.
/// We don't care about the style, we always and uniformly use {} in
/// struct literal syntax for making struct and enum variant values.
pub fn fields_to_vec(fields: syn::Fields) -> Vec<syn::Field> {
    use syn::Fields::*;
    match fields {
        Named(fields) => fields.named.into_iter().collect(),
        Unnamed(fields) => fields.unnamed.into_iter().collect(),
        Unit => vec![],
    }
}

/// Returns true iff the given type is the literal unit type `()`.
/// This is treated the same way by `syn` as a 0-tuple.
pub fn is_unit_type<T: Borrow<syn::Type>>(ty: T) -> bool {
    ty.borrow() == &parse_quote!(())
}

/// Returns the `Self` type (in the literal syntactic sense).
pub fn self_ty() -> syn::Type {
    parse_quote!(Self)
}

//==============================================================================
// Paths:
//==============================================================================

type CommaPS = syn::punctuated::Punctuated<syn::PathSegment, Token![::]>;

/// Returns true iff the path is simple, i.e:
/// just a :: separated list of identifiers.
fn is_path_simple(path: &syn::Path) -> bool {
    path.segments.iter().all(|ps| ps.arguments.is_empty())
}

/// Returns true iff lhs matches the rhs.
fn eq_simple_pathseg(lhs: &str, rhs: &CommaPS) -> bool {
    lhs.split("::")
        .filter(|s| !s.trim().is_empty())
        .eq(rhs.iter().map(|ps| ps.ident.to_string()))
}

/// Returns true iff lhs matches the given simple Path.
pub fn eq_simple_path(mut lhs: &str, rhs: &syn::Path) -> bool {
    if !is_path_simple(rhs) {
        return false;
    }

    if rhs.leading_colon.is_some() {
        if !lhs.starts_with("::") {
            return false;
        }
        lhs = &lhs[2..];
    }

    eq_simple_pathseg(lhs, &rhs.segments)
}

/// Returns true iff the given path matches any of given
/// paths specified as string slices.
pub fn match_pathsegs(path: &syn::Path, against: &[&str]) -> bool {
    against.iter().any(|needle| eq_simple_path(needle, path))
}

/// Returns true iff the given `PathArguments` is one that has one type
/// applied to it.
fn pseg_has_single_tyvar(pp: &syn::PathSegment) -> bool {
    use syn::GenericArgument::Type;
    use syn::PathArguments::AngleBracketed;
    if let AngleBracketed(ab) = &pp.arguments {
        if let Some(Type(_)) = match_singleton(ab.args.iter()) {
            return true;
        }
    }
    false
}

/// Returns true iff the given type is of the form `PhantomData<TY>` where
/// `TY` can be substituted for any type, including type variables.
pub fn is_phantom_data(path: &syn::Path) -> bool {
    let segs = &path.segments;
    if segs.is_empty() {
        return false;
    }

    let mut path = path.clone();
    let lseg = path.segments.pop().unwrap().into_value();

    &lseg.ident == "PhantomData"
        && pseg_has_single_tyvar(&lseg)
        && match_pathsegs(
            &path,
            &[
                // We hedge a bet that user will never declare
                // their own type named PhantomData.
                // This may give errors, but is worth it usability-wise.
                "",
                "marker",
                "std::marker",
                "core::marker",
                "::std::marker",
                "::core::marker",
            ],
        )
}

/// Extracts a simple non-global path of length 1.
pub fn extract_simple_path(path: &syn::Path) -> Option<&syn::Ident> {
    match_singleton(&path.segments)
        .filter(|f| !path_is_global(path) && f.arguments.is_empty())
        .map(|f| &f.ident)
}

/// Does the path have a leading `::`?
pub fn path_is_global(path: &syn::Path) -> bool {
    path.leading_colon.is_some()
}

//==============================================================================
// General Rust utilities:
//==============================================================================

/// Returns `Some(x)` iff the iterable is singleton and otherwise None.
pub fn match_singleton<T>(it: impl IntoIterator<Item = T>) -> Option<T> {
    let mut it = it.into_iter();
    it.next().filter(|_| it.next().is_none())
}