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