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())
}