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