2018-07-17 18:24:48 -05:00
use std ::collections ::hash_map ::DefaultHasher ;
use std ::env ;
use std ::fmt ;
use std ::hash ::{ Hash , Hasher } ;
2018-06-25 10:41:33 -07:00
use std ::iter ::FromIterator ;
2018-10-18 08:43:36 -07:00
use std ::sync ::atomic ::AtomicBool ;
use std ::sync ::atomic ::AtomicUsize ;
2018-07-17 18:24:48 -05:00
use std ::sync ::atomic ::Ordering ::SeqCst ;
2018-06-25 10:41:33 -07:00
2019-03-26 08:00:16 -07:00
use crate ::ast ;
2018-06-25 10:41:33 -07:00
use proc_macro2 ::{ self , Ident } ;
use syn ;
fn is_rust_keyword ( name : & str ) -> bool {
match name {
" abstract " | " alignof " | " as " | " become " | " box " | " break " | " const " | " continue "
| " crate " | " do " | " else " | " enum " | " extern " | " false " | " final " | " fn " | " for " | " if "
| " impl " | " in " | " let " | " loop " | " macro " | " match " | " mod " | " move " | " mut "
| " offsetof " | " override " | " priv " | " proc " | " pub " | " pure " | " ref " | " return "
| " Self " | " self " | " sizeof " | " static " | " struct " | " super " | " trait " | " true "
| " type " | " typeof " | " unsafe " | " unsized " | " use " | " virtual " | " where " | " while "
| " yield " | " bool " | " _ " = > true ,
_ = > false ,
}
}
// Create an `Ident`, possibly mangling it if it conflicts with a Rust keyword.
pub fn rust_ident ( name : & str ) -> Ident {
2018-07-30 00:07:19 +01:00
if name = = " " {
panic! ( " tried to create empty Ident (from \" \" ) " ) ;
} else if is_rust_keyword ( name ) {
2018-06-25 10:41:33 -07:00
Ident ::new ( & format! ( " {} _ " , name ) , proc_macro2 ::Span ::call_site ( ) )
2019-03-26 08:16:53 -07:00
// we didn't historically have `async` in the `is_rust_keyword` list above,
// so for backwards compatibility reasons we need to generate an `async`
// identifier as well, but we'll be sure to use a raw identifier to ease
// compatibility with the 2018 edition.
//
// Note, though, that `proc-macro` doesn't support a normal way to create a
// raw identifier. To get around that we do some wonky parsing to
// roundaboutly create one.
} else if name = = " async " {
let ident = " r#async "
. parse ::< proc_macro2 ::TokenStream > ( )
. unwrap ( )
. into_iter ( )
. next ( )
. unwrap ( ) ;
match ident {
proc_macro2 ::TokenTree ::Ident ( i ) = > i ,
_ = > unreachable! ( ) ,
}
2018-08-04 00:19:06 +03:00
} else if name . chars ( ) . next ( ) . unwrap ( ) . is_ascii_digit ( ) {
Ident ::new ( & format! ( " N {} " , name ) , proc_macro2 ::Span ::call_site ( ) )
2018-06-25 10:41:33 -07:00
} else {
raw_ident ( name )
}
}
// Create an `Ident` without checking to see if it conflicts with a Rust
// keyword.
pub fn raw_ident ( name : & str ) -> Ident {
Ident ::new ( name , proc_macro2 ::Span ::call_site ( ) )
}
/// Create a path type from the given segments. For example an iterator yielding
/// the idents `[foo, bar, baz]` will result in the path type `foo::bar::baz`.
pub fn simple_path_ty < I > ( segments : I ) -> syn ::Type
2018-07-11 11:07:03 -07:00
where
I : IntoIterator < Item = Ident > ,
{
path_ty ( false , segments )
}
/// Create a global path type from the given segments. For example an iterator
/// yielding the idents `[foo, bar, baz]` will result in the path type
/// `::foo::bar::baz`.
pub fn leading_colon_path_ty < I > ( segments : I ) -> syn ::Type
where
I : IntoIterator < Item = Ident > ,
{
path_ty ( true , segments )
}
fn path_ty < I > ( leading_colon : bool , segments : I ) -> syn ::Type
2018-06-25 10:41:33 -07:00
where
I : IntoIterator < Item = Ident > ,
{
let segments : Vec < _ > = segments
. into_iter ( )
. map ( | i | syn ::PathSegment {
ident : i ,
arguments : syn ::PathArguments ::None ,
2018-11-27 12:07:59 -08:00
} )
. collect ( ) ;
2018-06-25 10:41:33 -07:00
syn ::TypePath {
qself : None ,
path : syn ::Path {
2018-07-11 11:07:03 -07:00
leading_colon : if leading_colon {
Some ( Default ::default ( ) )
} else {
None
} ,
2018-06-25 10:41:33 -07:00
segments : syn ::punctuated ::Punctuated ::from_iter ( segments ) ,
} ,
2018-11-27 12:07:59 -08:00
}
. into ( )
2018-06-25 10:41:33 -07:00
}
pub fn ident_ty ( ident : Ident ) -> syn ::Type {
simple_path_ty ( Some ( ident ) )
}
pub fn wrap_import_function ( function : ast ::ImportFunction ) -> ast ::Import {
2020-02-26 19:00:11 -03:30
let unstable_api = function . unstable_api ;
2018-06-25 10:41:33 -07:00
ast ::Import {
2019-02-25 11:11:30 -08:00
module : ast ::ImportModule ::None ,
2018-06-25 10:41:33 -07:00
js_namespace : None ,
kind : ast ::ImportKind ::Function ( function ) ,
2020-02-26 19:00:11 -03:30
unstable_api ,
2018-06-25 10:41:33 -07:00
}
}
2018-07-17 18:24:48 -05:00
/// Small utility used when generating symbol names.
///
/// Hashes the public field here along with a few cargo-set env vars to
/// distinguish between runs of the procedural macro.
2018-09-05 12:55:30 -07:00
#[ derive(Debug) ]
2018-07-17 18:24:48 -05:00
pub struct ShortHash < T > ( pub T ) ;
impl < T : Hash > fmt ::Display for ShortHash < T > {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2019-02-15 11:58:48 +00:00
static HASHED : AtomicBool = AtomicBool ::new ( false ) ;
static HASH : AtomicUsize = AtomicUsize ::new ( 0 ) ;
2018-07-17 18:24:48 -05:00
// Try to amortize the cost of loading env vars a lot as we're gonna be
// hashing for a lot of symbols.
if ! HASHED . load ( SeqCst ) {
let mut h = DefaultHasher ::new ( ) ;
env ::var ( " CARGO_PKG_NAME " )
. expect ( " should have CARGO_PKG_NAME env var " )
. hash ( & mut h ) ;
env ::var ( " CARGO_PKG_VERSION " )
. expect ( " should have CARGO_PKG_VERSION env var " )
. hash ( & mut h ) ;
// This may chop off 32 bits on 32-bit platforms, but that's ok, we
// just want something to mix in below anyway.
HASH . store ( h . finish ( ) as usize , SeqCst ) ;
HASHED . store ( true , SeqCst ) ;
}
let mut h = DefaultHasher ::new ( ) ;
HASH . load ( SeqCst ) . hash ( & mut h ) ;
self . 0. hash ( & mut h ) ;
write! ( f , " {:016x} " , h . finish ( ) )
}
}
2020-02-26 19:00:11 -03:30
/// Create syn attribute for `#[cfg(web_sys_unstable_apis)]` and a doc comment.
pub fn unstable_api_attrs ( ) -> proc_macro2 ::TokenStream {
quote ::quote! (
#[ cfg(web_sys_unstable_apis) ]
#[ doc = " \n \n *This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as [described in the `wasm-bindgen` guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html)* " ]
)
}
pub fn maybe_unstable_api_attr ( unstable_api : bool ) -> Option < proc_macro2 ::TokenStream > {
if unstable_api {
Some ( unstable_api_attrs ( ) )
} else {
None
}
}