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