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("ed).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