1 use std::cell::Cell;
2 use std::char;
3 use std::str::Chars;
4 
5 use ast::OperationKind;
6 use backend::ast;
7 use backend::util::{ident_ty, ShortHash};
8 use backend::Diagnostic;
9 use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
10 use quote::ToTokens;
11 use shared;
12 use syn;
13 use syn::parse::{Parse, ParseStream, Result as SynResult};
14 use syn::spanned::Spanned;
15 
16 thread_local!(static ATTRS: AttributeParseState = Default::default());
17 
18 #[derive(Default)]
19 struct AttributeParseState {
20     parsed: Cell<usize>,
21     checks: Cell<usize>,
22 }
23 
24 /// Parsed attributes from a `#[wasm_bindgen(..)]`.
25 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
26 pub struct BindgenAttrs {
27     /// List of parsed attributes
28     pub attrs: Vec<(Cell<bool>, BindgenAttr)>,
29 }
30 
31 macro_rules! attrgen {
32     ($mac:ident) => {
33         $mac! {
34             (catch, Catch(Span)),
35             (constructor, Constructor(Span)),
36             (method, Method(Span)),
37             (static_method_of, StaticMethodOf(Span, Ident)),
38             (js_namespace, JsNamespace(Span, Vec<String>, Vec<Span>)),
39             (module, Module(Span, String, Span)),
40             (raw_module, RawModule(Span, String, Span)),
41             (inline_js, InlineJs(Span, String, Span)),
42             (getter, Getter(Span, Option<Ident>)),
43             (setter, Setter(Span, Option<Ident>)),
44             (indexing_getter, IndexingGetter(Span)),
45             (indexing_setter, IndexingSetter(Span)),
46             (indexing_deleter, IndexingDeleter(Span)),
47             (structural, Structural(Span)),
48             (r#final, Final(Span)),
49             (readonly, Readonly(Span)),
50             (js_name, JsName(Span, String, Span)),
51             (js_class, JsClass(Span, String, Span)),
52             (inspectable, Inspectable(Span)),
53             (is_type_of, IsTypeOf(Span, syn::Expr)),
54             (extends, Extends(Span, syn::Path)),
55             (no_deref, NoDeref(Span)),
56             (vendor_prefix, VendorPrefix(Span, Ident)),
57             (variadic, Variadic(Span)),
58             (typescript_custom_section, TypescriptCustomSection(Span)),
59             (skip_typescript, SkipTypescript(Span)),
60             (start, Start(Span)),
61             (skip, Skip(Span)),
62             (typescript_type, TypeScriptType(Span, String, Span)),
63             (getter_with_clone, GetterWithClone(Span)),
64 
65             // For testing purposes only.
66             (assert_no_shim, AssertNoShim(Span)),
67         }
68     };
69 }
70 
71 macro_rules! methods {
72     ($(($name:ident, $variant:ident($($contents:tt)*)),)*) => {
73         $(methods!(@method $name, $variant($($contents)*));)*
74 
75         #[cfg(feature = "strict-macro")]
76         fn check_used(self) -> Result<(), Diagnostic> {
77             // Account for the fact this method was called
78             ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
79 
80             let mut errors = Vec::new();
81             for (used, attr) in self.attrs.iter() {
82                 if used.get() {
83                     continue
84                 }
85                 // The check below causes rustc to crash on powerpc64 platforms
86                 // with an LLVM error. To avoid this, we instead use #[cfg()]
87                 // and duplicate the function below. See #58516 for details.
88                 /*if !cfg!(feature = "strict-macro") {
89                     continue
90                 }*/
91                 let span = match attr {
92                     $(BindgenAttr::$variant(span, ..) => span,)*
93                 };
94                 errors.push(Diagnostic::span_error(*span, "unused #[wasm_bindgen] attribute"));
95             }
96             Diagnostic::from_vec(errors)
97         }
98 
99         #[cfg(not(feature = "strict-macro"))]
100         fn check_used(self) -> Result<(), Diagnostic> {
101             // Account for the fact this method was called
102             ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
103             Ok(())
104         }
105     };
106 
107     (@method $name:ident, $variant:ident(Span, String, Span)) => {
108         fn $name(&self) -> Option<(&str, Span)> {
109             self.attrs
110                 .iter()
111                 .filter_map(|a| match &a.1 {
112                     BindgenAttr::$variant(_, s, span) => {
113                         a.0.set(true);
114                         Some((&s[..], *span))
115                     }
116                     _ => None,
117                 })
118                 .next()
119         }
120     };
121 
122     (@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => {
123         fn $name(&self) -> Option<(&[String], &[Span])> {
124             self.attrs
125                 .iter()
126                 .filter_map(|a| match &a.1 {
127                     BindgenAttr::$variant(_, ss, spans) => {
128                         a.0.set(true);
129                         Some((&ss[..], &spans[..]))
130                     }
131                     _ => None,
132                 })
133                 .next()
134         }
135     };
136 
137     (@method $name:ident, $variant:ident(Span, $($other:tt)*)) => {
138         #[allow(unused)]
139         fn $name(&self) -> Option<&$($other)*> {
140             self.attrs
141                 .iter()
142                 .filter_map(|a| match &a.1 {
143                     BindgenAttr::$variant(_, s) => {
144                         a.0.set(true);
145                         Some(s)
146                     }
147                     _ => None,
148                 })
149                 .next()
150         }
151     };
152 
153     (@method $name:ident, $variant:ident($($other:tt)*)) => {
154         #[allow(unused)]
155         fn $name(&self) -> Option<&$($other)*> {
156             self.attrs
157                 .iter()
158                 .filter_map(|a| match &a.1 {
159                     BindgenAttr::$variant(s) => {
160                         a.0.set(true);
161                         Some(s)
162                     }
163                     _ => None,
164                 })
165                 .next()
166         }
167     };
168 }
169 
170 impl BindgenAttrs {
171     /// Find and parse the wasm_bindgen attributes.
find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic>172     fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> {
173         let mut ret = BindgenAttrs::default();
174         loop {
175             let pos = attrs
176                 .iter()
177                 .enumerate()
178                 .find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen")
179                 .map(|a| a.0);
180             let pos = match pos {
181                 Some(i) => i,
182                 None => return Ok(ret),
183             };
184             let attr = attrs.remove(pos);
185             let mut tts = attr.tokens.clone().into_iter();
186             let group = match tts.next() {
187                 Some(TokenTree::Group(d)) => d,
188                 Some(_) => bail_span!(attr, "malformed #[wasm_bindgen] attribute"),
189                 None => continue,
190             };
191             if tts.next().is_some() {
192                 bail_span!(attr, "malformed #[wasm_bindgen] attribute");
193             }
194             if group.delimiter() != Delimiter::Parenthesis {
195                 bail_span!(attr, "malformed #[wasm_bindgen] attribute");
196             }
197             let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
198             ret.attrs.extend(attrs.attrs.drain(..));
199             attrs.check_used()?;
200         }
201     }
202 
203     attrgen!(methods);
204 }
205 
206 impl Default for BindgenAttrs {
default() -> BindgenAttrs207     fn default() -> BindgenAttrs {
208         // Add 1 to the list of parsed attribute sets. We'll use this counter to
209         // sanity check that we call `check_used` an appropriate number of
210         // times.
211         ATTRS.with(|state| state.parsed.set(state.parsed.get() + 1));
212         BindgenAttrs { attrs: Vec::new() }
213     }
214 }
215 
216 impl Parse for BindgenAttrs {
parse(input: ParseStream) -> SynResult<Self>217     fn parse(input: ParseStream) -> SynResult<Self> {
218         let mut attrs = BindgenAttrs::default();
219         if input.is_empty() {
220             return Ok(attrs);
221         }
222 
223         let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
224         attrs.attrs = opts.into_iter().map(|c| (Cell::new(false), c)).collect();
225         Ok(attrs)
226     }
227 }
228 
229 macro_rules! gen_bindgen_attr {
230     ($(($method:ident, $($variants:tt)*),)*) => {
231         /// The possible attributes in the `#[wasm_bindgen]`.
232         #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
233         pub enum BindgenAttr {
234             $($($variants)*,)*
235         }
236     }
237 }
238 attrgen!(gen_bindgen_attr);
239 
240 impl Parse for BindgenAttr {
parse(input: ParseStream) -> SynResult<Self>241     fn parse(input: ParseStream) -> SynResult<Self> {
242         let original = input.fork();
243         let attr: AnyIdent = input.parse()?;
244         let attr = attr.0;
245         let attr_span = attr.span();
246         let attr_string = attr.to_string();
247         let raw_attr_string = format!("r#{}", attr_string);
248 
249         macro_rules! parsers {
250             ($(($name:ident, $($contents:tt)*),)*) => {
251                 $(
252                     if attr_string == stringify!($name) || raw_attr_string == stringify!($name) {
253                         parsers!(
254                             @parser
255                             $($contents)*
256                         );
257                     }
258                 )*
259             };
260 
261             (@parser $variant:ident(Span)) => ({
262                 return Ok(BindgenAttr::$variant(attr_span));
263             });
264 
265             (@parser $variant:ident(Span, Ident)) => ({
266                 input.parse::<Token![=]>()?;
267                 let ident = input.parse::<AnyIdent>()?.0;
268                 return Ok(BindgenAttr::$variant(attr_span, ident))
269             });
270 
271             (@parser $variant:ident(Span, Option<Ident>)) => ({
272                 if input.parse::<Token![=]>().is_ok() {
273                     let ident = input.parse::<AnyIdent>()?.0;
274                     return Ok(BindgenAttr::$variant(attr_span, Some(ident)))
275                 } else {
276                     return Ok(BindgenAttr::$variant(attr_span, None));
277                 }
278             });
279 
280             (@parser $variant:ident(Span, syn::Path)) => ({
281                 input.parse::<Token![=]>()?;
282                 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
283             });
284 
285             (@parser $variant:ident(Span, syn::Expr)) => ({
286                 input.parse::<Token![=]>()?;
287                 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
288             });
289 
290             (@parser $variant:ident(Span, String, Span)) => ({
291                 input.parse::<Token![=]>()?;
292                 let (val, span) = match input.parse::<syn::LitStr>() {
293                     Ok(str) => (str.value(), str.span()),
294                     Err(_) => {
295                         let ident = input.parse::<AnyIdent>()?.0;
296                         (ident.to_string(), ident.span())
297                     }
298                 };
299                 return Ok(BindgenAttr::$variant(attr_span, val, span))
300             });
301 
302             (@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({
303                 input.parse::<Token![=]>()?;
304                 let (vals, spans) = match input.parse::<syn::ExprArray>() {
305                     Ok(exprs) => {
306                         let mut vals = vec![];
307                         let mut spans = vec![];
308 
309                         for expr in exprs.elems.iter() {
310                             if let syn::Expr::Lit(syn::ExprLit {
311                                 lit: syn::Lit::Str(ref str),
312                                 ..
313                             }) = expr {
314                                 vals.push(str.value());
315                                 spans.push(str.span());
316                             } else {
317                                 return Err(syn::Error::new(expr.span(), "expected string literals"));
318                             }
319                         }
320 
321                         (vals, spans)
322                     },
323                     Err(_) => {
324                         let ident = input.parse::<AnyIdent>()?.0;
325                         (vec![ident.to_string()], vec![ident.span()])
326                     }
327                 };
328                 return Ok(BindgenAttr::$variant(attr_span, vals, spans))
329             });
330         }
331 
332         attrgen!(parsers);
333 
334         return Err(original.error("unknown attribute"));
335     }
336 }
337 
338 struct AnyIdent(Ident);
339 
340 impl Parse for AnyIdent {
parse(input: ParseStream) -> SynResult<Self>341     fn parse(input: ParseStream) -> SynResult<Self> {
342         input.step(|cursor| match cursor.ident() {
343             Some((ident, remaining)) => Ok((AnyIdent(ident), remaining)),
344             None => Err(cursor.error("expected an identifier")),
345         })
346     }
347 }
348 
349 /// Conversion trait with context.
350 ///
351 /// Used to convert syn tokens into an AST, that we can then use to generate glue code. The context
352 /// (`Ctx`) is used to pass in the attributes from the `#[wasm_bindgen]`, if needed.
353 trait ConvertToAst<Ctx> {
354     /// What we are converting to.
355     type Target;
356     /// Convert into our target.
357     ///
358     /// Since this is used in a procedural macro, use panic to fail.
convert(self, context: Ctx) -> Result<Self::Target, Diagnostic>359     fn convert(self, context: Ctx) -> Result<Self::Target, Diagnostic>;
360 }
361 
362 impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
363     type Target = ast::Struct;
364 
convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic>365     fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
366         if self.generics.params.len() > 0 {
367             bail_span!(
368                 self.generics,
369                 "structs with #[wasm_bindgen] cannot have lifetime or \
370                  type parameters currently"
371             );
372         }
373         let mut fields = Vec::new();
374         let js_name = attrs
375             .js_name()
376             .map(|s| s.0.to_string())
377             .unwrap_or(self.ident.to_string());
378         let is_inspectable = attrs.inspectable().is_some();
379         let getter_with_clone = attrs.getter_with_clone().is_some();
380         for (i, field) in self.fields.iter_mut().enumerate() {
381             match field.vis {
382                 syn::Visibility::Public(..) => {}
383                 _ => continue,
384             }
385             let (js_field_name, member) = match &field.ident {
386                 Some(ident) => (ident.to_string(), syn::Member::Named(ident.clone())),
387                 None => (i.to_string(), syn::Member::Unnamed(i.into())),
388             };
389 
390             let attrs = BindgenAttrs::find(&mut field.attrs)?;
391             assert_not_variadic(&attrs)?;
392             if attrs.skip().is_some() {
393                 attrs.check_used()?;
394                 continue;
395             }
396 
397             let js_field_name = match attrs.js_name() {
398                 Some((name, _)) => name.to_string(),
399                 None => js_field_name,
400             };
401 
402             let comments = extract_doc_comments(&field.attrs);
403             let getter = shared::struct_field_get(&js_name, &js_field_name);
404             let setter = shared::struct_field_set(&js_name, &js_field_name);
405 
406             fields.push(ast::StructField {
407                 rust_name: member,
408                 js_name: js_field_name,
409                 struct_name: self.ident.clone(),
410                 readonly: attrs.readonly().is_some(),
411                 ty: field.ty.clone(),
412                 getter: Ident::new(&getter, Span::call_site()),
413                 setter: Ident::new(&setter, Span::call_site()),
414                 comments,
415                 generate_typescript: attrs.skip_typescript().is_none(),
416                 getter_with_clone: getter_with_clone || attrs.getter_with_clone().is_some(),
417             });
418             attrs.check_used()?;
419         }
420         let generate_typescript = attrs.skip_typescript().is_none();
421         let comments: Vec<String> = extract_doc_comments(&self.attrs);
422         attrs.check_used()?;
423         Ok(ast::Struct {
424             rust_name: self.ident.clone(),
425             js_name,
426             fields,
427             comments,
428             is_inspectable,
429             generate_typescript,
430         })
431     }
432 }
433 
get_ty(mut ty: &syn::Type) -> &syn::Type434 fn get_ty(mut ty: &syn::Type) -> &syn::Type {
435     while let syn::Type::Group(g) = ty {
436         ty = &g.elem;
437     }
438 
439     ty
440 }
441 
get_expr(mut expr: &syn::Expr) -> &syn::Expr442 fn get_expr(mut expr: &syn::Expr) -> &syn::Expr {
443     while let syn::Expr::Group(g) = expr {
444         expr = &g.expr;
445     }
446 
447     expr
448 }
449 
450 impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignItemFn {
451     type Target = ast::ImportKind;
452 
convert( self, (opts, module): (BindgenAttrs, &'a ast::ImportModule), ) -> Result<Self::Target, Diagnostic>453     fn convert(
454         self,
455         (opts, module): (BindgenAttrs, &'a ast::ImportModule),
456     ) -> Result<Self::Target, Diagnostic> {
457         let wasm = function_from_decl(
458             &self.sig.ident,
459             &opts,
460             self.sig.clone(),
461             self.attrs.clone(),
462             self.vis.clone(),
463             false,
464             None,
465         )?
466         .0;
467         let catch = opts.catch().is_some();
468         let variadic = opts.variadic().is_some();
469         let js_ret = if catch {
470             // TODO: this assumes a whole bunch:
471             //
472             // * The outer type is actually a `Result`
473             // * The error type is a `JsValue`
474             // * The actual type is the first type parameter
475             //
476             // should probably fix this one day...
477             extract_first_ty_param(wasm.ret.as_ref())?
478         } else {
479             wasm.ret.clone()
480         };
481 
482         let operation_kind = operation_kind(&opts);
483 
484         let kind = if opts.method().is_some() {
485             let class = wasm.arguments.get(0).ok_or_else(|| {
486                 err_span!(self, "imported methods must have at least one argument")
487             })?;
488             let class = match get_ty(&class.ty) {
489                 syn::Type::Reference(syn::TypeReference {
490                     mutability: None,
491                     elem,
492                     ..
493                 }) => &**elem,
494                 _ => bail_span!(
495                     class.ty,
496                     "first argument of method must be a shared reference"
497                 ),
498             };
499             let class_name = match get_ty(class) {
500                 syn::Type::Path(syn::TypePath {
501                     qself: None,
502                     ref path,
503                 }) => path,
504                 _ => bail_span!(class, "first argument of method must be a path"),
505             };
506             let class_name = extract_path_ident(class_name)?;
507             let class_name = opts
508                 .js_class()
509                 .map(|p| p.0.into())
510                 .unwrap_or_else(|| class_name.to_string());
511 
512             let kind = ast::MethodKind::Operation(ast::Operation {
513                 is_static: false,
514                 kind: operation_kind,
515             });
516 
517             ast::ImportFunctionKind::Method {
518                 class: class_name,
519                 ty: class.clone(),
520                 kind,
521             }
522         } else if let Some(cls) = opts.static_method_of() {
523             let class = opts
524                 .js_class()
525                 .map(|p| p.0.into())
526                 .unwrap_or_else(|| cls.to_string());
527             let ty = ident_ty(cls.clone());
528 
529             let kind = ast::MethodKind::Operation(ast::Operation {
530                 is_static: true,
531                 kind: operation_kind,
532             });
533 
534             ast::ImportFunctionKind::Method { class, ty, kind }
535         } else if opts.constructor().is_some() {
536             let class = match js_ret {
537                 Some(ref ty) => ty,
538                 _ => bail_span!(self, "constructor returns must be bare types"),
539             };
540             let class_name = match get_ty(class) {
541                 syn::Type::Path(syn::TypePath {
542                     qself: None,
543                     ref path,
544                 }) => path,
545                 _ => bail_span!(self, "return value of constructor must be a bare path"),
546             };
547             let class_name = extract_path_ident(class_name)?;
548             let class_name = opts
549                 .js_class()
550                 .map(|p| p.0.into())
551                 .unwrap_or_else(|| class_name.to_string());
552 
553             ast::ImportFunctionKind::Method {
554                 class: class_name.to_string(),
555                 ty: class.clone(),
556                 kind: ast::MethodKind::Constructor,
557             }
558         } else {
559             ast::ImportFunctionKind::Normal
560         };
561 
562         let shim = {
563             let ns = match kind {
564                 ast::ImportFunctionKind::Normal => (0, "n"),
565                 ast::ImportFunctionKind::Method { ref class, .. } => (1, &class[..]),
566             };
567             let data = (ns, &self.sig.ident, module);
568             format!(
569                 "__wbg_{}_{}",
570                 wasm.name
571                     .chars()
572                     .filter(|c| c.is_ascii_alphanumeric())
573                     .collect::<String>(),
574                 ShortHash(data)
575             )
576         };
577         if let Some(span) = opts.r#final() {
578             if opts.structural().is_some() {
579                 let msg = "cannot specify both `structural` and `final`";
580                 return Err(Diagnostic::span_error(*span, msg));
581             }
582         }
583         let assert_no_shim = opts.assert_no_shim().is_some();
584         let ret = ast::ImportKind::Function(ast::ImportFunction {
585             function: wasm,
586             assert_no_shim,
587             kind,
588             js_ret,
589             catch,
590             variadic,
591             structural: opts.structural().is_some() || opts.r#final().is_none(),
592             rust_name: self.sig.ident.clone(),
593             shim: Ident::new(&shim, Span::call_site()),
594             doc_comment: None,
595         });
596         opts.check_used()?;
597 
598         Ok(ret)
599     }
600 }
601 
602 impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
603     type Target = ast::ImportKind;
604 
convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic>605     fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
606         assert_not_variadic(&attrs)?;
607         let js_name = attrs
608             .js_name()
609             .map(|s| s.0)
610             .map_or_else(|| self.ident.to_string(), |s| s.to_string());
611         let typescript_type = attrs.typescript_type().map(|s| s.0.to_string());
612         let is_type_of = attrs.is_type_of().cloned();
613         let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
614         let mut extends = Vec::new();
615         let mut vendor_prefixes = Vec::new();
616         let no_deref = attrs.no_deref().is_some();
617         for (used, attr) in attrs.attrs.iter() {
618             match attr {
619                 BindgenAttr::Extends(_, e) => {
620                     extends.push(e.clone());
621                     used.set(true);
622                 }
623                 BindgenAttr::VendorPrefix(_, e) => {
624                     vendor_prefixes.push(e.clone());
625                     used.set(true);
626                 }
627                 _ => {}
628             }
629         }
630         attrs.check_used()?;
631         Ok(ast::ImportKind::Type(ast::ImportType {
632             vis: self.vis,
633             attrs: self.attrs,
634             doc_comment: None,
635             instanceof_shim: shim,
636             is_type_of,
637             rust_name: self.ident,
638             typescript_type,
639             js_name,
640             extends,
641             vendor_prefixes,
642             no_deref,
643         }))
644     }
645 }
646 
647 impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignItemStatic {
648     type Target = ast::ImportKind;
649 
convert( self, (opts, module): (BindgenAttrs, &'a ast::ImportModule), ) -> Result<Self::Target, Diagnostic>650     fn convert(
651         self,
652         (opts, module): (BindgenAttrs, &'a ast::ImportModule),
653     ) -> Result<Self::Target, Diagnostic> {
654         if self.mutability.is_some() {
655             bail_span!(self.mutability, "cannot import mutable globals yet")
656         }
657         assert_not_variadic(&opts)?;
658         let default_name = self.ident.to_string();
659         let js_name = opts
660             .js_name()
661             .map(|p| p.0)
662             .unwrap_or(&default_name)
663             .to_string();
664         let shim = format!(
665             "__wbg_static_accessor_{}_{}",
666             self.ident,
667             ShortHash((&js_name, module, &self.ident)),
668         );
669         opts.check_used()?;
670         Ok(ast::ImportKind::Static(ast::ImportStatic {
671             ty: *self.ty,
672             vis: self.vis,
673             rust_name: self.ident.clone(),
674             js_name,
675             shim: Ident::new(&shim, Span::call_site()),
676         }))
677     }
678 }
679 
680 impl ConvertToAst<BindgenAttrs> for syn::ItemFn {
681     type Target = ast::Function;
682 
convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic>683     fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
684         match self.vis {
685             syn::Visibility::Public(_) => {}
686             _ => bail_span!(self, "can only #[wasm_bindgen] public functions"),
687         }
688         if self.sig.constness.is_some() {
689             bail_span!(
690                 self.sig.constness,
691                 "can only #[wasm_bindgen] non-const functions"
692             );
693         }
694         if self.sig.unsafety.is_some() {
695             bail_span!(self.sig.unsafety, "can only #[wasm_bindgen] safe functions");
696         }
697         assert_not_variadic(&attrs)?;
698 
699         let ret = function_from_decl(
700             &self.sig.ident,
701             &attrs,
702             self.sig.clone(),
703             self.attrs,
704             self.vis,
705             false,
706             None,
707         )?;
708         attrs.check_used()?;
709         Ok(ret.0)
710     }
711 }
712 
713 /// Construct a function (and gets the self type if appropriate) for our AST from a syn function.
function_from_decl( decl_name: &syn::Ident, opts: &BindgenAttrs, sig: syn::Signature, attrs: Vec<syn::Attribute>, vis: syn::Visibility, allow_self: bool, self_ty: Option<&Ident>, ) -> Result<(ast::Function, Option<ast::MethodSelf>), Diagnostic>714 fn function_from_decl(
715     decl_name: &syn::Ident,
716     opts: &BindgenAttrs,
717     sig: syn::Signature,
718     attrs: Vec<syn::Attribute>,
719     vis: syn::Visibility,
720     allow_self: bool,
721     self_ty: Option<&Ident>,
722 ) -> Result<(ast::Function, Option<ast::MethodSelf>), Diagnostic> {
723     if sig.variadic.is_some() {
724         bail_span!(sig.variadic, "can't #[wasm_bindgen] variadic functions");
725     }
726     if sig.generics.params.len() > 0 {
727         bail_span!(
728             sig.generics,
729             "can't #[wasm_bindgen] functions with lifetime or type parameters",
730         );
731     }
732 
733     assert_no_lifetimes(&sig)?;
734 
735     let syn::Signature { inputs, output, .. } = sig;
736 
737     let replace_self = |t: syn::Type| {
738         let self_ty = match self_ty {
739             Some(i) => i,
740             None => return t,
741         };
742         let path = match get_ty(&t) {
743             syn::Type::Path(syn::TypePath { qself: None, path }) => path.clone(),
744             other => return other.clone(),
745         };
746         let new_path = if path.segments.len() == 1 && path.segments[0].ident == "Self" {
747             self_ty.clone().into()
748         } else {
749             path
750         };
751         syn::Type::Path(syn::TypePath {
752             qself: None,
753             path: new_path,
754         })
755     };
756 
757     let mut method_self = None;
758     let arguments = inputs
759         .into_iter()
760         .filter_map(|arg| match arg {
761             syn::FnArg::Typed(mut c) => {
762                 c.ty = Box::new(replace_self(*c.ty));
763                 Some(c)
764             }
765             syn::FnArg::Receiver(r) => {
766                 if !allow_self {
767                     panic!("arguments cannot be `self`")
768                 }
769                 assert!(method_self.is_none());
770                 if r.reference.is_none() {
771                     method_self = Some(ast::MethodSelf::ByValue);
772                 } else if r.mutability.is_some() {
773                     method_self = Some(ast::MethodSelf::RefMutable);
774                 } else {
775                     method_self = Some(ast::MethodSelf::RefShared);
776                 }
777                 None
778             }
779         })
780         .collect::<Vec<_>>();
781 
782     let ret = match output {
783         syn::ReturnType::Default => None,
784         syn::ReturnType::Type(_, ty) => Some(replace_self(*ty)),
785     };
786 
787     let (name, name_span, renamed_via_js_name) =
788         if let Some((js_name, js_name_span)) = opts.js_name() {
789             let kind = operation_kind(&opts);
790             let prefix = match kind {
791                 OperationKind::Setter(_) => "set_",
792                 _ => "",
793             };
794             (
795                 format!("{}{}", prefix, js_name.to_string()),
796                 js_name_span,
797                 true,
798             )
799         } else {
800             (decl_name.to_string(), decl_name.span(), false)
801         };
802     Ok((
803         ast::Function {
804             arguments,
805             name_span,
806             name,
807             renamed_via_js_name,
808             ret,
809             rust_attrs: attrs,
810             rust_vis: vis,
811             r#async: sig.asyncness.is_some(),
812             generate_typescript: opts.skip_typescript().is_none(),
813         },
814         method_self,
815     ))
816 }
817 
818 pub(crate) trait MacroParse<Ctx> {
819     /// Parse the contents of an object into our AST, with a context if necessary.
820     ///
821     /// The context is used to have access to the attributes on `#[wasm_bindgen]`, and to allow
822     /// writing to the output `TokenStream`.
macro_parse(self, program: &mut ast::Program, context: Ctx) -> Result<(), Diagnostic>823     fn macro_parse(self, program: &mut ast::Program, context: Ctx) -> Result<(), Diagnostic>;
824 }
825 
826 impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
macro_parse( self, program: &mut ast::Program, (opts, tokens): (Option<BindgenAttrs>, &'a mut TokenStream), ) -> Result<(), Diagnostic>827     fn macro_parse(
828         self,
829         program: &mut ast::Program,
830         (opts, tokens): (Option<BindgenAttrs>, &'a mut TokenStream),
831     ) -> Result<(), Diagnostic> {
832         match self {
833             syn::Item::Fn(mut f) => {
834                 let no_mangle = f
835                     .attrs
836                     .iter()
837                     .enumerate()
838                     .filter_map(|(i, m)| m.parse_meta().ok().map(|m| (i, m)))
839                     .find(|(_, m)| m.path().is_ident("no_mangle"));
840                 match no_mangle {
841                     Some((i, _)) => {
842                         f.attrs.remove(i);
843                     }
844                     _ => {}
845                 }
846                 let comments = extract_doc_comments(&f.attrs);
847                 f.to_tokens(tokens);
848                 let opts = opts.unwrap_or_default();
849                 if opts.start().is_some() {
850                     if f.sig.generics.params.len() > 0 {
851                         bail_span!(&f.sig.generics, "the start function cannot have generics",);
852                     }
853                     if f.sig.inputs.len() > 0 {
854                         bail_span!(&f.sig.inputs, "the start function cannot have arguments",);
855                     }
856                 }
857                 let method_kind = ast::MethodKind::Operation(ast::Operation {
858                     is_static: true,
859                     kind: operation_kind(&opts),
860                 });
861                 let rust_name = f.sig.ident.clone();
862                 let start = opts.start().is_some();
863                 program.exports.push(ast::Export {
864                     comments,
865                     function: f.convert(opts)?,
866                     js_class: None,
867                     method_kind,
868                     method_self: None,
869                     rust_class: None,
870                     rust_name,
871                     start,
872                 });
873             }
874             syn::Item::Struct(mut s) => {
875                 let opts = opts.unwrap_or_default();
876                 program.structs.push((&mut s).convert(opts)?);
877                 s.to_tokens(tokens);
878             }
879             syn::Item::Impl(mut i) => {
880                 let opts = opts.unwrap_or_default();
881                 (&mut i).macro_parse(program, opts)?;
882                 i.to_tokens(tokens);
883             }
884             syn::Item::ForeignMod(mut f) => {
885                 let opts = match opts {
886                     Some(opts) => opts,
887                     None => BindgenAttrs::find(&mut f.attrs)?,
888                 };
889                 f.macro_parse(program, opts)?;
890             }
891             syn::Item::Enum(mut e) => {
892                 let opts = match opts {
893                     Some(opts) => opts,
894                     None => BindgenAttrs::find(&mut e.attrs)?,
895                 };
896                 e.macro_parse(program, (tokens, opts))?;
897             }
898             syn::Item::Const(mut c) => {
899                 let opts = match opts {
900                     Some(opts) => opts,
901                     None => BindgenAttrs::find(&mut c.attrs)?,
902                 };
903                 c.macro_parse(program, opts)?;
904             }
905             _ => {
906                 bail_span!(
907                     self,
908                     "#[wasm_bindgen] can only be applied to a function, \
909                      struct, enum, impl, or extern block",
910                 );
911             }
912         }
913 
914         Ok(())
915     }
916 }
917 
918 impl<'a> MacroParse<BindgenAttrs> for &'a mut syn::ItemImpl {
macro_parse( self, _program: &mut ast::Program, opts: BindgenAttrs, ) -> Result<(), Diagnostic>919     fn macro_parse(
920         self,
921         _program: &mut ast::Program,
922         opts: BindgenAttrs,
923     ) -> Result<(), Diagnostic> {
924         if self.defaultness.is_some() {
925             bail_span!(
926                 self.defaultness,
927                 "#[wasm_bindgen] default impls are not supported"
928             );
929         }
930         if self.unsafety.is_some() {
931             bail_span!(
932                 self.unsafety,
933                 "#[wasm_bindgen] unsafe impls are not supported"
934             );
935         }
936         if let Some((_, path, _)) = &self.trait_ {
937             bail_span!(path, "#[wasm_bindgen] trait impls are not supported");
938         }
939         if self.generics.params.len() > 0 {
940             bail_span!(
941                 self.generics,
942                 "#[wasm_bindgen] generic impls aren't supported"
943             );
944         }
945         let name = match get_ty(&self.self_ty) {
946             syn::Type::Path(syn::TypePath {
947                 qself: None,
948                 ref path,
949             }) => path,
950             _ => bail_span!(
951                 self.self_ty,
952                 "unsupported self type in #[wasm_bindgen] impl"
953             ),
954         };
955         let mut errors = Vec::new();
956         for item in self.items.iter_mut() {
957             if let Err(e) = prepare_for_impl_recursion(item, &name, &opts) {
958                 errors.push(e);
959             }
960         }
961         Diagnostic::from_vec(errors)?;
962         opts.check_used()?;
963         Ok(())
964     }
965 }
966 
967 // Prepare for recursion into an `impl` block. Here we want to attach an
968 // internal attribute, `__wasm_bindgen_class_marker`, with any metadata we need
969 // to pass from the impl to the impl item. Recursive macro expansion will then
970 // expand the `__wasm_bindgen_class_marker` attribute.
971 //
972 // Note that we currently do this because inner items may have things like cfgs
973 // on them, so we want to expand the impl first, let the insides get cfg'd, and
974 // then go for the rest.
prepare_for_impl_recursion( item: &mut syn::ImplItem, class: &syn::Path, impl_opts: &BindgenAttrs, ) -> Result<(), Diagnostic>975 fn prepare_for_impl_recursion(
976     item: &mut syn::ImplItem,
977     class: &syn::Path,
978     impl_opts: &BindgenAttrs,
979 ) -> Result<(), Diagnostic> {
980     let method = match item {
981         syn::ImplItem::Method(m) => m,
982         syn::ImplItem::Const(_) => {
983             bail_span!(
984                 &*item,
985                 "const definitions aren't supported with #[wasm_bindgen]"
986             );
987         }
988         syn::ImplItem::Type(_) => bail_span!(
989             &*item,
990             "type definitions in impls aren't supported with #[wasm_bindgen]"
991         ),
992         syn::ImplItem::Macro(_) => {
993             // In theory we want to allow this, but we have no way of expanding
994             // the macro and then placing our magical attributes on the expanded
995             // functions. As a result, just disallow it for now to hopefully
996             // ward off buggy results from this macro.
997             bail_span!(&*item, "macros in impls aren't supported");
998         }
999         syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
1000         other => bail_span!(other, "failed to parse this item as a known item"),
1001     };
1002 
1003     let ident = extract_path_ident(class)?;
1004 
1005     let js_class = impl_opts
1006         .js_class()
1007         .map(|s| s.0.to_string())
1008         .unwrap_or(ident.to_string());
1009 
1010     method.attrs.insert(
1011         0,
1012         syn::Attribute {
1013             pound_token: Default::default(),
1014             style: syn::AttrStyle::Outer,
1015             bracket_token: Default::default(),
1016             path: syn::parse_quote! { wasm_bindgen::prelude::__wasm_bindgen_class_marker },
1017             tokens: quote::quote! { (#class = #js_class) }.into(),
1018         },
1019     );
1020 
1021     Ok(())
1022 }
1023 
1024 impl<'a, 'b> MacroParse<(&'a Ident, &'a str)> for &'b mut syn::ImplItemMethod {
macro_parse( self, program: &mut ast::Program, (class, js_class): (&'a Ident, &'a str), ) -> Result<(), Diagnostic>1025     fn macro_parse(
1026         self,
1027         program: &mut ast::Program,
1028         (class, js_class): (&'a Ident, &'a str),
1029     ) -> Result<(), Diagnostic> {
1030         match self.vis {
1031             syn::Visibility::Public(_) => {}
1032             _ => return Ok(()),
1033         }
1034         if self.defaultness.is_some() {
1035             panic!("default methods are not supported");
1036         }
1037         if self.sig.constness.is_some() {
1038             bail_span!(
1039                 self.sig.constness,
1040                 "can only #[wasm_bindgen] non-const functions",
1041             );
1042         }
1043         if self.sig.unsafety.is_some() {
1044             bail_span!(self.sig.unsafety, "can only bindgen safe functions",);
1045         }
1046 
1047         let opts = BindgenAttrs::find(&mut self.attrs)?;
1048         let comments = extract_doc_comments(&self.attrs);
1049         let (function, method_self) = function_from_decl(
1050             &self.sig.ident,
1051             &opts,
1052             self.sig.clone(),
1053             self.attrs.clone(),
1054             self.vis.clone(),
1055             true,
1056             Some(class),
1057         )?;
1058         let method_kind = if opts.constructor().is_some() {
1059             ast::MethodKind::Constructor
1060         } else {
1061             let is_static = method_self.is_none();
1062             let kind = operation_kind(&opts);
1063             ast::MethodKind::Operation(ast::Operation { is_static, kind })
1064         };
1065         program.exports.push(ast::Export {
1066             comments,
1067             function,
1068             js_class: Some(js_class.to_string()),
1069             method_kind,
1070             method_self,
1071             rust_class: Some(class.clone()),
1072             rust_name: self.sig.ident.clone(),
1073             start: false,
1074         });
1075         opts.check_used()?;
1076         Ok(())
1077     }
1078 }
1079 
import_enum(enum_: syn::ItemEnum, program: &mut ast::Program) -> Result<(), Diagnostic>1080 fn import_enum(enum_: syn::ItemEnum, program: &mut ast::Program) -> Result<(), Diagnostic> {
1081     let mut variants = vec![];
1082     let mut variant_values = vec![];
1083 
1084     for v in enum_.variants.iter() {
1085         match v.fields {
1086             syn::Fields::Unit => (),
1087             _ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"),
1088         }
1089 
1090         let (_, expr) = match &v.discriminant {
1091             Some(pair) => pair,
1092             None => {
1093                 bail_span!(v, "all variants must have a value");
1094             }
1095         };
1096         match get_expr(expr) {
1097             syn::Expr::Lit(syn::ExprLit {
1098                 attrs: _,
1099                 lit: syn::Lit::Str(str_lit),
1100             }) => {
1101                 variants.push(v.ident.clone());
1102                 variant_values.push(str_lit.value());
1103             }
1104             expr => bail_span!(
1105                 expr,
1106                 "enums with #[wasm_bindgen] cannot mix string and non-string values",
1107             ),
1108         }
1109     }
1110 
1111     program.imports.push(ast::Import {
1112         module: ast::ImportModule::None,
1113         js_namespace: None,
1114         kind: ast::ImportKind::Enum(ast::ImportEnum {
1115             vis: enum_.vis,
1116             name: enum_.ident,
1117             variants,
1118             variant_values,
1119             rust_attrs: enum_.attrs,
1120         }),
1121     });
1122 
1123     Ok(())
1124 }
1125 
1126 impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum {
macro_parse( self, program: &mut ast::Program, (tokens, opts): (&'a mut TokenStream, BindgenAttrs), ) -> Result<(), Diagnostic>1127     fn macro_parse(
1128         self,
1129         program: &mut ast::Program,
1130         (tokens, opts): (&'a mut TokenStream, BindgenAttrs),
1131     ) -> Result<(), Diagnostic> {
1132         if self.variants.len() == 0 {
1133             bail_span!(self, "cannot export empty enums to JS");
1134         }
1135         let generate_typescript = opts.skip_typescript().is_none();
1136 
1137         // Check if the first value is a string literal
1138         if let Some((_, expr)) = &self.variants[0].discriminant {
1139             match get_expr(expr) {
1140                 syn::Expr::Lit(syn::ExprLit {
1141                     attrs: _,
1142                     lit: syn::Lit::Str(_),
1143                 }) => {
1144                     opts.check_used()?;
1145                     return import_enum(self, program);
1146                 }
1147                 _ => {}
1148             }
1149         }
1150         let js_name = opts
1151             .js_name()
1152             .map(|s| s.0)
1153             .map_or_else(|| self.ident.to_string(), |s| s.to_string());
1154         opts.check_used()?;
1155 
1156         let has_discriminant = self.variants[0].discriminant.is_some();
1157 
1158         match self.vis {
1159             syn::Visibility::Public(_) => {}
1160             _ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"),
1161         }
1162 
1163         let variants = self
1164             .variants
1165             .iter()
1166             .enumerate()
1167             .map(|(i, v)| {
1168                 match v.fields {
1169                     syn::Fields::Unit => (),
1170                     _ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"),
1171                 }
1172 
1173                 // Require that everything either has a discriminant or doesn't.
1174                 // We don't really want to get in the business of emulating how
1175                 // rustc assigns values to enums.
1176                 if v.discriminant.is_some() != has_discriminant {
1177                     bail_span!(
1178                         v,
1179                         "must either annotate discriminant of all variants or none"
1180                     );
1181                 }
1182 
1183                 let value = match &v.discriminant {
1184                     Some((_, expr)) => match get_expr(expr) {
1185                         syn::Expr::Lit(syn::ExprLit {
1186                             attrs: _,
1187                             lit: syn::Lit::Int(int_lit),
1188                         }) => match int_lit.base10_digits().parse::<u32>() {
1189                             Ok(v) => v,
1190                             Err(_) => {
1191                                 bail_span!(
1192                                     int_lit,
1193                                     "enums with #[wasm_bindgen] can only support \
1194                                  numbers that can be represented as u32"
1195                                 );
1196                             }
1197                         },
1198                         expr => bail_span!(
1199                             expr,
1200                             "enums with #[wasm_bindgen] may only have \
1201                              number literal values",
1202                         ),
1203                     },
1204                     None => i as u32,
1205                 };
1206 
1207                 let comments = extract_doc_comments(&v.attrs);
1208                 Ok(ast::Variant {
1209                     name: v.ident.clone(),
1210                     value,
1211                     comments,
1212                 })
1213             })
1214             .collect::<Result<Vec<_>, Diagnostic>>()?;
1215 
1216         let mut values = variants.iter().map(|v| v.value).collect::<Vec<_>>();
1217         values.sort();
1218         let hole = values
1219             .windows(2)
1220             .filter_map(|window| {
1221                 if window[0] + 1 != window[1] {
1222                     Some(window[0] + 1)
1223                 } else {
1224                     None
1225                 }
1226             })
1227             .next()
1228             .unwrap_or(*values.last().unwrap() + 1);
1229         for value in values {
1230             assert!(hole != value);
1231         }
1232 
1233         let comments = extract_doc_comments(&self.attrs);
1234 
1235         self.to_tokens(tokens);
1236 
1237         program.enums.push(ast::Enum {
1238             rust_name: self.ident,
1239             js_name,
1240             variants,
1241             comments,
1242             hole,
1243             generate_typescript,
1244         });
1245         Ok(())
1246     }
1247 }
1248 
1249 impl MacroParse<BindgenAttrs> for syn::ItemConst {
macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic>1250     fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
1251         // Shortcut
1252         if opts.typescript_custom_section().is_none() {
1253             bail_span!(self, "#[wasm_bindgen] will not work on constants unless you are defining a #[wasm_bindgen(typescript_custom_section)].");
1254         }
1255 
1256         match get_expr(&self.expr) {
1257             syn::Expr::Lit(syn::ExprLit {
1258                 lit: syn::Lit::Str(litstr),
1259                 ..
1260             }) => {
1261                 program.typescript_custom_sections.push(litstr.value());
1262             }
1263             expr => {
1264                 bail_span!(expr, "Expected a string literal to be used with #[wasm_bindgen(typescript_custom_section)].");
1265             }
1266         }
1267 
1268         opts.check_used()?;
1269 
1270         Ok(())
1271     }
1272 }
1273 
1274 impl MacroParse<BindgenAttrs> for syn::ItemForeignMod {
macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic>1275     fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
1276         let mut errors = Vec::new();
1277         match self.abi.name {
1278             Some(ref l) if l.value() == "C" => {}
1279             None => {}
1280             Some(ref other) => {
1281                 errors.push(err_span!(
1282                     other,
1283                     "only foreign mods with the `C` ABI are allowed"
1284                 ));
1285             }
1286         }
1287         let module = if let Some((name, span)) = opts.module() {
1288             if opts.inline_js().is_some() {
1289                 let msg = "cannot specify both `module` and `inline_js`";
1290                 errors.push(Diagnostic::span_error(span, msg));
1291             }
1292             if opts.raw_module().is_some() {
1293                 let msg = "cannot specify both `module` and `raw_module`";
1294                 errors.push(Diagnostic::span_error(span, msg));
1295             }
1296             ast::ImportModule::Named(name.to_string(), span)
1297         } else if let Some((name, span)) = opts.raw_module() {
1298             if opts.inline_js().is_some() {
1299                 let msg = "cannot specify both `raw_module` and `inline_js`";
1300                 errors.push(Diagnostic::span_error(span, msg));
1301             }
1302             ast::ImportModule::RawNamed(name.to_string(), span)
1303         } else if let Some((js, span)) = opts.inline_js() {
1304             let i = program.inline_js.len();
1305             program.inline_js.push(js.to_string());
1306             ast::ImportModule::Inline(i, span)
1307         } else {
1308             ast::ImportModule::None
1309         };
1310         for item in self.items.into_iter() {
1311             if let Err(e) = item.macro_parse(program, module.clone()) {
1312                 errors.push(e);
1313             }
1314         }
1315         Diagnostic::from_vec(errors)?;
1316         opts.check_used()?;
1317         Ok(())
1318     }
1319 }
1320 
1321 impl MacroParse<ast::ImportModule> for syn::ForeignItem {
macro_parse( mut self, program: &mut ast::Program, module: ast::ImportModule, ) -> Result<(), Diagnostic>1322     fn macro_parse(
1323         mut self,
1324         program: &mut ast::Program,
1325         module: ast::ImportModule,
1326     ) -> Result<(), Diagnostic> {
1327         let item_opts = {
1328             let attrs = match self {
1329                 syn::ForeignItem::Fn(ref mut f) => &mut f.attrs,
1330                 syn::ForeignItem::Type(ref mut t) => &mut t.attrs,
1331                 syn::ForeignItem::Static(ref mut s) => &mut s.attrs,
1332                 _ => panic!("only foreign functions/types allowed for now"),
1333             };
1334             BindgenAttrs::find(attrs)?
1335         };
1336         let js_namespace = item_opts.js_namespace().map(|(s, _)| s.to_owned());
1337         let kind = match self {
1338             syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?,
1339             syn::ForeignItem::Type(t) => t.convert(item_opts)?,
1340             syn::ForeignItem::Static(s) => s.convert((item_opts, &module))?,
1341             _ => panic!("only foreign functions/types allowed for now"),
1342         };
1343 
1344         program.imports.push(ast::Import {
1345             module,
1346             js_namespace,
1347             kind,
1348         });
1349 
1350         Ok(())
1351     }
1352 }
1353 
1354 /// Get the first type parameter of a generic type, errors on incorrect input.
extract_first_ty_param(ty: Option<&syn::Type>) -> Result<Option<syn::Type>, Diagnostic>1355 fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result<Option<syn::Type>, Diagnostic> {
1356     let t = match ty {
1357         Some(t) => t,
1358         None => return Ok(None),
1359     };
1360     let path = match *get_ty(&t) {
1361         syn::Type::Path(syn::TypePath {
1362             qself: None,
1363             ref path,
1364         }) => path,
1365         _ => bail_span!(t, "must be Result<...>"),
1366     };
1367     let seg = path
1368         .segments
1369         .last()
1370         .ok_or_else(|| err_span!(t, "must have at least one segment"))?;
1371     let generics = match seg.arguments {
1372         syn::PathArguments::AngleBracketed(ref t) => t,
1373         _ => bail_span!(t, "must be Result<...>"),
1374     };
1375     let generic = generics
1376         .args
1377         .first()
1378         .ok_or_else(|| err_span!(t, "must have at least one generic parameter"))?;
1379     let ty = match generic {
1380         syn::GenericArgument::Type(t) => t,
1381         other => bail_span!(other, "must be a type parameter"),
1382     };
1383     match get_ty(&ty) {
1384         syn::Type::Tuple(t) if t.elems.len() == 0 => return Ok(None),
1385         _ => {}
1386     }
1387     Ok(Some(ty.clone()))
1388 }
1389 
1390 /// Extract the documentation comments from a Vec of attributes
extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String>1391 fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
1392     attrs
1393         .iter()
1394         .filter_map(|a| {
1395             // if the path segments include an ident of "doc" we know this
1396             // this is a doc comment
1397             if a.path.segments.iter().any(|s| s.ident.to_string() == "doc") {
1398                 Some(
1399                     // We want to filter out any Puncts so just grab the Literals
1400                     a.tokens.clone().into_iter().filter_map(|t| match t {
1401                         TokenTree::Literal(lit) => {
1402                             let quoted = lit.to_string();
1403                             Some(try_unescape(&quoted).unwrap_or_else(|| quoted))
1404                         }
1405                         _ => None,
1406                     }),
1407                 )
1408             } else {
1409                 None
1410             }
1411         })
1412         //Fold up the [[String]] iter we created into Vec<String>
1413         .fold(vec![], |mut acc, a| {
1414             acc.extend(a);
1415             acc
1416         })
1417 }
1418 
1419 // Unescapes a quoted string. char::escape_debug() was used to escape the text.
try_unescape(s: &str) -> Option<String>1420 fn try_unescape(s: &str) -> Option<String> {
1421     if s.is_empty() {
1422         return Some(String::new());
1423     }
1424     let mut result = String::with_capacity(s.len());
1425     let mut chars = s.chars();
1426     for i in 0.. {
1427         let c = match chars.next() {
1428             Some(c) => c,
1429             None => {
1430                 if result.ends_with('"') {
1431                     result.pop();
1432                 }
1433                 return Some(result);
1434             }
1435         };
1436         if i == 0 && c == '"' {
1437             // ignore it
1438         } else if c == '\\' {
1439             let c = chars.next()?;
1440             match c {
1441                 't' => result.push('\t'),
1442                 'r' => result.push('\r'),
1443                 'n' => result.push('\n'),
1444                 '\\' | '\'' | '"' => result.push(c),
1445                 'u' => {
1446                     if chars.next() != Some('{') {
1447                         return None;
1448                     }
1449                     let (c, next) = unescape_unicode(&mut chars)?;
1450                     result.push(c);
1451                     if next != '}' {
1452                         return None;
1453                     }
1454                 }
1455                 _ => return None,
1456             }
1457         } else {
1458             result.push(c);
1459         }
1460     }
1461     None
1462 }
1463 
unescape_unicode(chars: &mut Chars) -> Option<(char, char)>1464 fn unescape_unicode(chars: &mut Chars) -> Option<(char, char)> {
1465     let mut value = 0;
1466     for i in 0..7 {
1467         let c = chars.next()?;
1468         let num = if c >= '0' && c <= '9' {
1469             c as u32 - '0' as u32
1470         } else if c >= 'a' && c <= 'f' {
1471             c as u32 - 'a' as u32 + 10
1472         } else if c >= 'A' && c <= 'F' {
1473             c as u32 - 'A' as u32 + 10
1474         } else {
1475             if i == 0 {
1476                 return None;
1477             }
1478             let decoded = char::from_u32(value)?;
1479             return Some((decoded, c));
1480         };
1481         if i >= 6 {
1482             return None;
1483         }
1484         value = (value << 4) | num;
1485     }
1486     None
1487 }
1488 
1489 /// Check there are no lifetimes on the function.
assert_no_lifetimes(sig: &syn::Signature) -> Result<(), Diagnostic>1490 fn assert_no_lifetimes(sig: &syn::Signature) -> Result<(), Diagnostic> {
1491     struct Walk {
1492         diagnostics: Vec<Diagnostic>,
1493     }
1494 
1495     impl<'ast> syn::visit::Visit<'ast> for Walk {
1496         fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) {
1497             self.diagnostics.push(err_span!(
1498                 &*i,
1499                 "it is currently not sound to use lifetimes in function \
1500                  signatures"
1501             ));
1502         }
1503     }
1504     let mut walk = Walk {
1505         diagnostics: Vec::new(),
1506     };
1507     syn::visit::Visit::visit_signature(&mut walk, sig);
1508     Diagnostic::from_vec(walk.diagnostics)
1509 }
1510 
1511 /// This method always fails if the BindgenAttrs contain variadic
assert_not_variadic(attrs: &BindgenAttrs) -> Result<(), Diagnostic>1512 fn assert_not_variadic(attrs: &BindgenAttrs) -> Result<(), Diagnostic> {
1513     if let Some(span) = attrs.variadic() {
1514         let msg = "the `variadic` attribute can only be applied to imported \
1515                    (`extern`) functions";
1516         return Err(Diagnostic::span_error(*span, msg));
1517     }
1518     Ok(())
1519 }
1520 
1521 /// Extracts the last ident from the path
extract_path_ident(path: &syn::Path) -> Result<Ident, Diagnostic>1522 fn extract_path_ident(path: &syn::Path) -> Result<Ident, Diagnostic> {
1523     for segment in path.segments.iter() {
1524         match segment.arguments {
1525             syn::PathArguments::None => {}
1526             _ => bail_span!(path, "paths with type parameters are not supported yet"),
1527         }
1528     }
1529 
1530     match path.segments.last() {
1531         Some(value) => Ok(value.ident.clone()),
1532         None => {
1533             bail_span!(path, "empty idents are not supported");
1534         }
1535     }
1536 }
1537 
reset_attrs_used()1538 pub fn reset_attrs_used() {
1539     ATTRS.with(|state| {
1540         state.parsed.set(0);
1541         state.checks.set(0);
1542     })
1543 }
1544 
assert_all_attrs_checked()1545 pub fn assert_all_attrs_checked() {
1546     ATTRS.with(|state| {
1547         assert_eq!(state.parsed.get(), state.checks.get());
1548     })
1549 }
1550 
operation_kind(opts: &BindgenAttrs) -> ast::OperationKind1551 fn operation_kind(opts: &BindgenAttrs) -> ast::OperationKind {
1552     let mut operation_kind = ast::OperationKind::Regular;
1553     if let Some(g) = opts.getter() {
1554         operation_kind = ast::OperationKind::Getter(g.clone());
1555     }
1556     if let Some(s) = opts.setter() {
1557         operation_kind = ast::OperationKind::Setter(s.clone());
1558     }
1559     if opts.indexing_getter().is_some() {
1560         operation_kind = ast::OperationKind::IndexingGetter;
1561     }
1562     if opts.indexing_setter().is_some() {
1563         operation_kind = ast::OperationKind::IndexingSetter;
1564     }
1565     if opts.indexing_deleter().is_some() {
1566         operation_kind = ast::OperationKind::IndexingDeleter;
1567     }
1568     operation_kind
1569 }
1570