1use std::cell::{Cell, RefCell};
2use std::collections::HashMap;
3use std::str::Chars;
4use std::{char, iter};
5
6use ast::OperationKind;
7use proc_macro2::{Ident, Span, TokenStream, TokenTree};
8use quote::ToTokens;
9use syn::ext::IdentExt;
10use syn::parse::{Parse, ParseStream, Result as SynResult};
11use syn::spanned::Spanned;
12use syn::visit_mut::VisitMut;
13use syn::Token;
14use syn::{ItemFn, Lit, MacroDelimiter, ReturnType};
15use wasm_bindgen_shared::identifier::{is_js_keyword, is_non_value_js_keyword, is_valid_ident};
16
17use crate::ast::{self, ThreadLocal};
18use crate::hash::ShortHash;
19use crate::ClassMarker;
20use crate::Diagnostic;
21
22thread_local!(static ATTRS: AttributeParseState = Default::default());
23
24fn check_js_comment_close(str: &str, span: Span) -> Result<(), Diagnostic> {
26 if str.contains("*/") {
27 Err(Diagnostic::span_error(
28 span,
29 "contains comment close syntax",
30 ))
31 } else {
32 Ok(())
33 }
34}
35
36fn check_invalid_type(str: &str, span: Span) -> Result<(), Diagnostic> {
38 if is_js_keyword(str) {
39 return Err(Diagnostic::span_error(span, "collides with JS keyword"));
40 }
41 check_js_comment_close(str, span)?;
42 Ok(())
43}
44
45#[derive(Default)]
46struct AttributeParseState {
47 parsed: Cell<usize>,
48 checks: Cell<usize>,
49 unused_attrs: RefCell<Vec<UnusedState>>,
50}
51
52struct UnusedState {
53 error: bool,
54 ident: Ident,
55}
56
57#[cfg_attr(feature = "extra-traits", derive(Debug))]
59pub struct BindgenAttrs {
60 pub attrs: Vec<(Cell<bool>, BindgenAttr)>,
62}
63
64#[cfg_attr(feature = "extra-traits", derive(Debug))]
70#[derive(Clone)]
71pub struct JsNamespace(Vec<String>);
72
73macro_rules! attrgen {
74 ($mac:ident) => {
75 $mac! {
76 (catch, false, Catch(Span)),
77 (constructor, false, Constructor(Span)),
78 (method, false, Method(Span)),
79 (r#this, false, This(Span)),
80 (static_method_of, false, StaticMethodOf(Span, Ident)),
81 (js_namespace, false, JsNamespace(Span, JsNamespace, Vec<Span>)),
82 (module, true, Module(Span, String, Span)),
83 (raw_module, true, RawModule(Span, String, Span)),
84 (inline_js, true, InlineJs(Span, String, Span)),
85 (getter, false, Getter(Span, Option<String>)),
86 (setter, false, Setter(Span, Option<String>)),
87 (indexing_getter, false, IndexingGetter(Span)),
88 (indexing_setter, false, IndexingSetter(Span)),
89 (indexing_deleter, false, IndexingDeleter(Span)),
90 (structural, false, Structural(Span)),
91 (r#final, false, Final(Span)),
92 (readonly, false, Readonly(Span)),
93 (js_name, false, JsName(Span, String, Span)),
94 (js_class, false, JsClass(Span, String, Span)),
95 (reexport, false, Reexport(Span, Option<String>)),
96 (inspectable, false, Inspectable(Span)),
97 (is_type_of, false, IsTypeOf(Span, syn::Expr)),
98 (extends, false, Extends(Span, syn::Path)),
99 (no_deref, false, NoDeref(Span)),
100 (no_upcast, false, NoUpcast(Span)),
101 (no_promising, false, NoPromising(Span)),
102 (vendor_prefix, false, VendorPrefix(Span, Ident)),
103 (variadic, false, Variadic(Span)),
104 (typescript_custom_section, false, TypescriptCustomSection(Span)),
105 (skip_typescript, false, SkipTypescript(Span)),
106 (skip_jsdoc, false, SkipJsDoc(Span)),
107 (private, false, Hide(Span)),
108 (main, false, Main(Span)),
109 (start, false, Start(Span)),
110 (wasm_bindgen, false, WasmBindgen(Span, syn::Path)),
111 (js_sys, false, JsSys(Span, syn::Path)),
112 (wasm_bindgen_futures, false, WasmBindgenFutures(Span, syn::Path)),
113 (skip, false, Skip(Span)),
114 (typescript_type, false, TypeScriptType(Span, String, Span)),
115 (getter_with_clone, false, GetterWithClone(Span)),
116 (static_string, false, StaticString(Span)),
117 (thread_local, false, ThreadLocal(Span)),
118 (thread_local_v2, false, ThreadLocalV2(Span)),
119 (unchecked_return_type, true, ReturnType(Span, String, Span)),
120 (return_description, true, ReturnDesc(Span, String, Span)),
121 (unchecked_param_type, true, ParamType(Span, String, Span)),
122 (unchecked_optional_param_type, true, OptionalParamType(Span, String, Span)),
123 (param_description, true, ParamDesc(Span, String, Span)),
124
125 (assert_no_shim, false, AssertNoShim(Span)),
127 }
128 };
129}
130
131macro_rules! methods {
132 ($(($name:ident, $invalid_unused:literal, $variant:ident($($contents:tt)*)),)*) => {
133 $(methods!(@method $name, $variant($($contents)*));)*
134
135 fn enforce_used(self) -> Result<(), Diagnostic> {
136 ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
138
139 let mut errors = Vec::new();
140 for (used, attr) in self.attrs.iter() {
141 if used.get() {
142 continue
143 }
144 let span = match attr {
145 $(BindgenAttr::$variant(span, ..) => span,)*
146 };
147 errors.push(Diagnostic::span_error(*span, "unused wasm_bindgen attribute"));
148 }
149 Diagnostic::from_vec(errors)
150 }
151
152 fn check_used(self) {
153 ATTRS.with(|state| {
155 state.checks.set(state.checks.get() + 1);
156
157 state.unused_attrs.borrow_mut().extend(
158 self.attrs
159 .iter()
160 .filter_map(|(used, attr)| if used.get() { None } else { Some(attr) })
161 .map(|attr| {
162 match attr {
163 $(BindgenAttr::$variant(span, ..) => {
164 UnusedState {
165 error: $invalid_unused,
166 ident: syn::parse_quote_spanned!(*span => $name)
167 }
168 })*
169 }
170 })
171 );
172 });
173 }
174 };
175
176 (@method $name:ident, $variant:ident(Span, String, Span)) => {
177 pub(crate) fn $name(&self) -> Option<(&str, Span)> {
178 self.attrs
179 .iter()
180 .find_map(|a| match &a.1 {
181 BindgenAttr::$variant(_, s, span) => {
182 a.0.set(true);
183 Some((&s[..], *span))
184 }
185 _ => None,
186 })
187 }
188 };
189
190 (@method $name:ident, $variant:ident(Span, JsNamespace, Vec<Span>)) => {
191 pub(crate) fn $name(&self) -> Option<(JsNamespace, &[Span])> {
192 self.attrs
193 .iter()
194 .find_map(|a| match &a.1 {
195 BindgenAttr::$variant(_, ss, spans) => {
196 a.0.set(true);
197 Some((ss.clone(), &spans[..]))
198 }
199 _ => None,
200 })
201 }
202 };
203
204 (@method $name:ident, $variant:ident(Span, $($other:tt)*)) => {
205 #[allow(unused)]
206 pub(crate) fn $name(&self) -> Option<&$($other)*> {
207 self.attrs
208 .iter()
209 .find_map(|a| match &a.1 {
210 BindgenAttr::$variant(_, s) => {
211 a.0.set(true);
212 Some(s)
213 }
214 _ => None,
215 })
216 }
217 };
218
219 (@method $name:ident, $variant:ident($($other:tt)*)) => {
220 #[allow(unused)]
221 pub(crate) fn $name(&self) -> Option<&$($other)*> {
222 self.attrs
223 .iter()
224 .find_map(|a| match &a.1 {
225 BindgenAttr::$variant(s) => {
226 a.0.set(true);
227 Some(s)
228 }
229 _ => None,
230 })
231 }
232 };
233}
234
235impl BindgenAttrs {
236 fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> {
238 let mut ret = BindgenAttrs::default();
239 loop {
240 let pos = attrs
241 .iter()
242 .enumerate()
243 .find(|&(_, m)| m.path().segments[0].ident == "wasm_bindgen")
244 .map(|a| a.0);
245 let pos = match pos {
246 Some(i) => i,
247 None => return Ok(ret),
248 };
249 let attr = attrs.remove(pos);
250 let tokens = match attr.meta {
251 syn::Meta::Path(_) => continue,
252 syn::Meta::List(syn::MetaList {
253 delimiter: MacroDelimiter::Paren(_),
254 tokens,
255 ..
256 }) => tokens,
257 syn::Meta::List(_) | syn::Meta::NameValue(_) => {
258 bail_span!(attr, "malformed #[wasm_bindgen] attribute")
259 }
260 };
261 let mut attrs: BindgenAttrs = syn::parse2(tokens)?;
262 ret.attrs.append(&mut attrs.attrs);
263 attrs.check_used();
264 }
265 }
266
267 fn get_thread_local(&self) -> Result<Option<ThreadLocal>, Diagnostic> {
268 let mut thread_local = self.thread_local_v2().map(|_| ThreadLocal::V2);
269
270 if let Some(span) = self.thread_local() {
271 if thread_local.is_some() {
272 return Err(Diagnostic::span_error(
273 *span,
274 "`thread_local` can't be used with `thread_local_v2`",
275 ));
276 } else {
277 thread_local = Some(ThreadLocal::V1)
278 }
279 }
280
281 Ok(thread_local)
282 }
283
284 attrgen!(methods);
285}
286
287impl Default for BindgenAttrs {
288 fn default() -> BindgenAttrs {
289 ATTRS.with(|state| state.parsed.set(state.parsed.get() + 1));
293 BindgenAttrs { attrs: Vec::new() }
294 }
295}
296
297impl Parse for BindgenAttrs {
298 fn parse(input: ParseStream) -> SynResult<Self> {
299 let mut attrs = BindgenAttrs::default();
300 if input.is_empty() {
301 return Ok(attrs);
302 }
303
304 let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
305 attrs.attrs = opts.into_iter().map(|c| (Cell::new(false), c)).collect();
306 Ok(attrs)
307 }
308}
309
310macro_rules! gen_bindgen_attr {
311 ($(($method:ident, $_:literal, $($variants:tt)*),)*) => {
312 #[cfg_attr(feature = "extra-traits", derive(Debug))]
314 pub enum BindgenAttr {
315 $($($variants)*,)*
316 }
317 }
318}
319attrgen!(gen_bindgen_attr);
320
321impl Parse for BindgenAttr {
322 fn parse(input: ParseStream) -> SynResult<Self> {
323 let original = input.fork();
324 let attr: AnyIdent = input.parse()?;
325 let attr = attr.0;
326 let attr_span = attr.span();
327 let attr_string = attr.to_string();
328 let raw_attr_string = format!("r#{attr_string}");
329
330 macro_rules! parsers {
331 ($(($name:ident, $_:literal, $($contents:tt)*),)*) => {
332 $(
333 if attr_string == stringify!($name) || raw_attr_string == stringify!($name) {
334 parsers!(
335 @parser
336 $($contents)*
337 );
338 }
339 )*
340 };
341
342 (@parser $variant:ident(Span)) => ({
343 return Ok(BindgenAttr::$variant(attr_span));
344 });
345
346 (@parser $variant:ident(Span, Ident)) => ({
347 input.parse::<Token![=]>()?;
348 let ident = input.parse::<AnyIdent>()?.0;
349 return Ok(BindgenAttr::$variant(attr_span, ident))
350 });
351
352 (@parser $variant:ident(Span, Option<String>)) => ({
353 if input.parse::<Token![=]>().is_ok() {
354 if input.peek(syn::LitStr) {
355 let litstr = input.parse::<syn::LitStr>()?;
356 return Ok(BindgenAttr::$variant(attr_span, Some(litstr.value())))
357 }
358
359 let ident = input.parse::<AnyIdent>()?.0;
360 return Ok(BindgenAttr::$variant(attr_span, Some(ident.to_string())))
361 } else {
362 return Ok(BindgenAttr::$variant(attr_span, None));
363 }
364 });
365
366 (@parser $variant:ident(Span, syn::Path)) => ({
367 input.parse::<Token![=]>()?;
368 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
369 });
370
371 (@parser $variant:ident(Span, syn::Expr)) => ({
372 input.parse::<Token![=]>()?;
373 return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
374 });
375
376 (@parser $variant:ident(Span, String, Span)) => ({
377 input.parse::<Token![=]>()?;
378 let (val, span) = match input.parse::<syn::LitStr>() {
379 Ok(str) => (str.value(), str.span()),
380 Err(_) => {
381 let ident = input.parse::<AnyIdent>()?.0;
382 (ident.to_string(), ident.span())
383 }
384 };
385 return Ok(BindgenAttr::$variant(attr_span, val, span))
386 });
387
388 (@parser $variant:ident(Span, JsNamespace, Vec<Span>)) => ({
389 input.parse::<Token![=]>()?;
390 let (vals, spans) = match input.parse::<syn::ExprArray>() {
391 Ok(exprs) => {
392 let mut vals = vec![];
393 let mut spans = vec![];
394
395 for expr in exprs.elems.iter() {
396 if let syn::Expr::Lit(syn::ExprLit {
397 lit: syn::Lit::Str(ref str),
398 ..
399 }) = expr {
400 vals.push(str.value());
401 spans.push(str.span());
402 } else {
403 return Err(syn::Error::new(expr.span(), "expected string literals"));
404 }
405 }
406
407 if vals.is_empty() {
408 return Err(syn::Error::new(exprs.span(), "Empty namespace lists are not allowed."));
409 }
410
411 (vals, spans)
412 },
413 Err(_) => match input.parse::<syn::LitStr>() {
415 Ok(str) => (vec![str.value()], vec![str.span()]),
416 Err(_) => {
417 let ident = input.parse::<AnyIdent>()?.0;
418 (vec![ident.to_string()], vec![ident.span()])
419 }
420 }
421 };
422
423 let first = &vals[0];
424 if is_non_value_js_keyword(first) && first != "default" {
425 let msg = format!("Namespace cannot start with the JS keyword `{}`", first);
426 return Err(syn::Error::new(spans[0], msg));
427 }
428
429 return Ok(BindgenAttr::$variant(attr_span, JsNamespace(vals), spans))
430 });
431 }
432
433 attrgen!(parsers);
434
435 Err(original.error(if attr_string.starts_with('_') {
436 "unknown attribute: it's safe to remove unused attributes entirely."
437 } else {
438 "unknown attribute"
439 }))
440 }
441}
442
443struct AnyIdent(Ident);
444
445impl Parse for AnyIdent {
446 fn parse(input: ParseStream) -> SynResult<Self> {
447 input.step(|cursor| match cursor.ident() {
448 Some((ident, remaining)) => Ok((AnyIdent(ident), remaining)),
449 None => Err(cursor.error("expected an identifier")),
450 })
451 }
452}
453
454pub(crate) trait ConvertToAst<Ctx> {
459 type Target;
461 fn convert(self, context: Ctx) -> Result<Self::Target, Diagnostic>;
465}
466
467impl ConvertToAst<&ast::Program> for &mut syn::ItemStruct {
468 type Target = ast::Struct;
469
470 fn convert(self, program: &ast::Program) -> Result<Self::Target, Diagnostic> {
471 if !self.generics.params.is_empty() {
472 bail_span!(
473 self.generics,
474 "structs with #[wasm_bindgen] cannot have lifetime or \
475 type parameters currently"
476 );
477 }
478 let attrs = BindgenAttrs::find(&mut self.attrs)?;
479
480 let _ = attrs.wasm_bindgen();
482
483 let mut fields = Vec::new();
484 let js_name = attrs
485 .js_name()
486 .map(|s| s.0.to_string())
487 .unwrap_or(self.ident.unraw().to_string());
488 if is_js_keyword(&js_name) && js_name != "default" {
489 bail_span!(
490 self.ident,
491 "struct cannot use the JS keyword `{}` as its name",
492 js_name
493 );
494 }
495
496 let is_inspectable = attrs.inspectable().is_some();
497 let getter_with_clone = attrs.getter_with_clone();
498 let js_namespace = attrs.js_namespace().map(|(ns, _)| ns.0);
499 let qualified_name = wasm_bindgen_shared::qualified_name(js_namespace.as_deref(), &js_name);
500 for (i, field) in self.fields.iter_mut().enumerate() {
501 match field.vis {
502 syn::Visibility::Public(..) => {}
503 _ => continue,
504 }
505 let (js_field_name, member) = match &field.ident {
506 Some(ident) => (ident.unraw().to_string(), syn::Member::Named(ident.clone())),
507 None => (i.to_string(), syn::Member::Unnamed(i.into())),
508 };
509
510 let attrs = BindgenAttrs::find(&mut field.attrs)?;
511 if attrs.skip().is_some() {
512 attrs.check_used();
513 continue;
514 }
515
516 let js_field_name = match attrs.js_name() {
517 Some((name, _)) => name.to_string(),
518 None => js_field_name,
519 };
520
521 let comments = extract_doc_comments(&field.attrs);
522 let getter = wasm_bindgen_shared::struct_field_get(&qualified_name, &js_field_name);
523 let setter = wasm_bindgen_shared::struct_field_set(&qualified_name, &js_field_name);
524
525 fields.push(ast::StructField {
526 rust_name: member,
527 js_name: js_field_name,
528 struct_name: self.ident.clone(),
529 readonly: attrs.readonly().is_some(),
530 ty: field.ty.clone(),
531 getter: Ident::new(&getter, Span::call_site()),
532 setter: Ident::new(&setter, Span::call_site()),
533 comments,
534 generate_typescript: attrs.skip_typescript().is_none(),
535 generate_jsdoc: attrs.skip_jsdoc().is_none(),
536 getter_with_clone: attrs.getter_with_clone().or(getter_with_clone).copied(),
537 wasm_bindgen: program.wasm_bindgen.clone(),
538 });
539 attrs.check_used();
540 }
541 let generate_typescript = attrs.skip_typescript().is_none();
542 let private = attrs.private().is_some();
543 let comments: Vec<String> = extract_doc_comments(&self.attrs);
544 attrs.check_used();
545 Ok(ast::Struct {
546 rust_name: self.ident.clone(),
547 js_name,
548 qualified_name,
549 fields,
550 comments,
551 is_inspectable,
552 generate_typescript,
553 private,
554 js_namespace,
555 wasm_bindgen: program.wasm_bindgen.clone(),
556 })
557 }
558}
559
560fn get_ty(mut ty: &syn::Type) -> &syn::Type {
561 while let syn::Type::Group(g) = ty {
562 ty = &g.elem;
563 }
564
565 ty
566}
567
568fn get_expr(mut expr: &syn::Expr) -> &syn::Expr {
569 while let syn::Expr::Group(g) = expr {
570 expr = &g.expr;
571 }
572
573 expr
574}
575
576impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule>)>
577 for syn::ForeignItemFn
578{
579 type Target = ast::ImportKind;
580
581 fn convert(
582 self,
583 (program, opts, module): (&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule>),
584 ) -> Result<Self::Target, Diagnostic> {
585 let (mut wasm, _) = function_from_decl(
586 &self.sig.ident,
587 &opts,
588 self.sig.clone(),
589 self.attrs.clone(),
590 self.vis.clone(),
591 FunctionPosition::Extern,
592 None,
593 )?;
594 let catch = opts.catch().is_some();
595 let variadic = opts.variadic().is_some();
596 let js_ret = if catch {
597 extract_first_ty_param(wasm.ret.as_ref().map(|ret| &ret.r#type))?
605 } else {
606 wasm.ret.as_ref().map(|ret| ret.r#type.clone())
607 };
608
609 let operation_kind = operation_kind(&opts);
610
611 let kind = if opts.method().is_some() {
612 let class = wasm.arguments.first().ok_or_else(|| {
613 err_span!(self, "imported methods must have at least one argument")
614 })?;
615 let class = match get_ty(&class.pat_type.ty) {
616 syn::Type::Reference(syn::TypeReference {
617 mutability: None,
618 elem,
619 ..
620 }) => &**elem,
621 _ => bail_span!(
622 class.pat_type.ty,
623 "first argument of method must be a shared reference"
624 ),
625 };
626 let class_ty = get_ty(class);
627 let js_class = opts.js_class().map(|p| p.0.to_string());
628 let kind = ast::MethodKind::Operation(ast::Operation {
629 is_static: false,
630 kind: operation_kind,
631 });
632
633 let class_name = match class_ty {
634 syn::Type::Path(syn::TypePath {
635 qself: None,
636 ref path,
637 }) => path,
638 _ => bail_span!(class_ty, "first argument of method must be a path"),
639 };
640
641 let class_name_str = js_class
642 .map(Ok)
643 .unwrap_or_else(|| extract_path_ident(class_name, true).map(|i| i.to_string()))?;
644
645 ast::ImportFunctionKind::Method {
646 class: class_name_str,
647 ty: class_ty.clone(),
648 kind,
649 }
650 } else if let Some(cls) = opts.static_method_of() {
651 let class = opts
652 .js_class()
653 .map(|p| p.0.into())
654 .unwrap_or_else(|| cls.to_string());
655
656 let ty = syn::Type::Path(syn::TypePath {
657 qself: None,
658 path: syn::Path {
659 leading_colon: None,
660 segments: std::iter::once(syn::PathSegment {
661 ident: cls.clone(),
662 arguments: syn::PathArguments::None,
663 })
664 .collect(),
665 },
666 });
667
668 let kind = ast::MethodKind::Operation(ast::Operation {
669 is_static: true,
670 kind: operation_kind,
671 });
672
673 ast::ImportFunctionKind::Method { class, ty, kind }
674 } else if opts.constructor().is_some() {
675 let class = match js_ret {
676 Some(ref ty) => ty,
677 _ => bail_span!(self, "constructor returns must be bare types"),
678 };
679 let class_name = match get_ty(class) {
680 syn::Type::Path(syn::TypePath {
681 qself: None,
682 ref path,
683 }) => path,
684 _ => bail_span!(self, "return value of constructor must be a bare path"),
685 };
686 let class_name = extract_path_ident(class_name, true)?;
687 let class_name = opts
688 .js_class()
689 .map(|p| p.0.into())
690 .unwrap_or_else(|| class_name.to_string());
691
692 ast::ImportFunctionKind::Method {
693 class: class_name,
694 ty: class.clone(),
695 kind: ast::MethodKind::Constructor,
696 }
697 } else {
698 ast::ImportFunctionKind::Normal
699 };
700
701 if opts.reexport().is_some() && matches!(kind, ast::ImportFunctionKind::Method { .. }) {
703 return Err(Diagnostic::span_error(
704 self.sig.ident.span(),
705 "`reexport` cannot be used on methods, constructors, or static methods. \
706 Use `reexport` on the type import instead.",
707 ));
708 }
709
710 let shim = {
711 let ns = match kind {
712 ast::ImportFunctionKind::Normal => (0, "n"),
713 ast::ImportFunctionKind::Method { ref class, .. } => (1, &class[..]),
714 };
715 let cfg_attrs: String = self
718 .attrs
719 .iter()
720 .filter(|attr| attr.path().is_ident("cfg"))
721 .map(|attr| attr.to_token_stream().to_string())
722 .collect();
723 let data = (
724 ns,
725 self.sig.to_token_stream().to_string(),
726 module,
727 cfg_attrs,
728 );
729 format!(
730 "__wbg_{}_{}",
731 wasm.name
732 .chars()
733 .filter(|&c| c.is_ascii_alphanumeric() || c == '_')
734 .collect::<String>(),
735 ShortHash(data)
736 )
737 };
738 if let Some(span) = opts.r#final() {
739 if opts.structural().is_some() {
740 let msg = "cannot specify both `structural` and `final`";
741 return Err(Diagnostic::span_error(*span, msg));
742 }
743 }
744 let assert_no_shim = opts.assert_no_shim().is_some();
745
746 let mut doc_comment = String::new();
747 wasm.rust_attrs.retain(|attr| {
749 fn get_docs(attr: &syn::Attribute) -> Option<String> {
752 if attr.path().is_ident("doc") {
753 if let syn::Meta::NameValue(syn::MetaNameValue {
754 value:
755 syn::Expr::Lit(syn::ExprLit {
756 lit: Lit::Str(str), ..
757 }),
758 ..
759 }) = &attr.meta
760 {
761 Some(str.value())
762 } else {
763 None
764 }
765 } else {
766 None
767 }
768 }
769
770 if let Some(docs) = get_docs(attr) {
771 if !doc_comment.is_empty() {
772 doc_comment.push('\n');
774 }
775 doc_comment.push_str(&docs);
777
778 false
780 } else {
781 true
782 }
783 });
784
785 validate_generics(&self.sig.generics)?;
786
787 let ret = ast::ImportKind::Function(ast::ImportFunction {
788 function: wasm,
789 assert_no_shim,
790 kind,
791 js_ret,
792 catch,
793 variadic,
794 structural: opts.structural().is_some() || opts.r#final().is_none(),
795 rust_name: self.sig.ident,
796 shim: Ident::new(&shim, Span::call_site()),
797 doc_comment,
798 wasm_bindgen: program.wasm_bindgen.clone(),
799 wasm_bindgen_futures: program.wasm_bindgen_futures.clone(),
800 generics: self.sig.generics,
801 });
802 opts.check_used();
803
804 Ok(ret)
805 }
806}
807
808impl ConvertToAst<(&ast::Program, BindgenAttrs)> for syn::ForeignItemType {
809 type Target = ast::ImportKind;
810
811 fn convert(
812 self,
813 (program, attrs): (&ast::Program, BindgenAttrs),
814 ) -> Result<Self::Target, Diagnostic> {
815 let js_name = attrs
816 .js_name()
817 .map(|s| s.0)
818 .map_or_else(|| self.ident.to_string(), |s| s.to_string());
819 let typescript_type = attrs.typescript_type().map(|s| s.0.to_string());
820 let is_type_of = attrs.is_type_of().cloned();
821 let shim = format!(
822 "__wbg_instanceof_{}_{}",
823 self.ident,
824 ShortHash((attrs.js_namespace().map(|(ns, _)| ns.0), &self.ident))
825 );
826 let mut extends = Vec::new();
827 let mut vendor_prefixes = Vec::new();
828 let no_deref = attrs.no_deref().is_some();
829 let no_upcast = attrs.no_upcast().is_some();
830 let no_promising = attrs.no_promising().is_some();
831 for (used, attr) in attrs.attrs.iter() {
832 match attr {
833 BindgenAttr::Extends(_, e) => {
834 extends.push(e.clone());
835 used.set(true);
836 }
837 BindgenAttr::VendorPrefix(_, e) => {
838 vendor_prefixes.push(e.clone());
839 used.set(true);
840 }
841 _ => {}
842 }
843 }
844
845 attrs.check_used();
846 validate_generics(&self.generics)?;
847
848 let mut generics = None;
851 for (n, param) in self.generics.type_params().enumerate() {
852 if param.default.is_none() {
853 let generics = generics.get_or_insert_with(|| self.generics.clone());
854 let type_param_mut = generics.type_params_mut().nth(n).unwrap();
855 type_param_mut.default = Some(syn::parse_quote! { JsValue });
856 }
857 }
858
859 Ok(ast::ImportKind::Type(ast::ImportType {
860 vis: self.vis,
861 attrs: self.attrs,
862 doc_comment: None,
863 instanceof_shim: shim,
864 is_type_of,
865 rust_name: self.ident,
866 typescript_type,
867 js_name,
868 extends,
869 vendor_prefixes,
870 no_deref,
871 no_upcast,
872 no_promising,
873 wasm_bindgen: program.wasm_bindgen.clone(),
874 generics: generics.unwrap_or(self.generics),
875 }))
876 }
877}
878
879impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule>)>
880 for syn::ForeignItemStatic
881{
882 type Target = ast::ImportKind;
883
884 fn convert(
885 self,
886 (program, opts, module): (&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule>),
887 ) -> Result<Self::Target, Diagnostic> {
888 if let syn::StaticMutability::Mut(_) = self.mutability {
889 bail_span!(self.mutability, "cannot import mutable globals yet")
890 }
891
892 if let Some(span) = opts.static_string() {
893 return Err(Diagnostic::span_error(
894 *span,
895 "static strings require a string literal",
896 ));
897 }
898
899 let default_name = self.ident.to_string();
900 let js_name = opts
901 .js_name()
902 .map(|p| p.0)
903 .unwrap_or(&default_name)
904 .to_string();
905 let shim = format!(
906 "__wbg_static_accessor_{}_{}",
907 self.ident,
908 ShortHash((&js_name, module, &self.ident)),
909 );
910 let thread_local = opts.get_thread_local()?;
911
912 opts.check_used();
913 Ok(ast::ImportKind::Static(ast::ImportStatic {
914 ty: *self.ty,
915 vis: self.vis,
916 rust_name: self.ident.clone(),
917 js_name,
918 shim: Ident::new(&shim, Span::call_site()),
919 wasm_bindgen: program.wasm_bindgen.clone(),
920 thread_local,
921 }))
922 }
923}
924
925impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule>)>
926 for syn::ItemStatic
927{
928 type Target = ast::ImportKind;
929
930 fn convert(
931 self,
932 (program, opts, module): (&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule>),
933 ) -> Result<Self::Target, Diagnostic> {
934 if let syn::StaticMutability::Mut(_) = self.mutability {
935 bail_span!(self.mutability, "cannot import mutable globals yet")
936 }
937
938 let string = if let syn::Expr::Lit(syn::ExprLit {
939 lit: syn::Lit::Str(string),
940 ..
941 }) = *self.expr.clone()
942 {
943 string.value()
944 } else {
945 bail_span!(
946 self.expr,
947 "statics with a value can only be string literals"
948 )
949 };
950
951 if opts.static_string().is_none() {
952 bail_span!(
953 self,
954 "static strings require `#[wasm_bindgen(static_string)]`"
955 )
956 }
957
958 let thread_local = if let Some(thread_local) = opts.get_thread_local()? {
959 thread_local
960 } else {
961 bail_span!(
962 self,
963 "static strings require `#[wasm_bindgen(thread_local_v2)]`"
964 )
965 };
966
967 let shim = format!(
968 "__wbg_string_{}_{}",
969 self.ident,
970 ShortHash((&module, &self.ident)),
971 );
972 opts.check_used();
973 Ok(ast::ImportKind::String(ast::ImportString {
974 ty: *self.ty,
975 vis: self.vis,
976 rust_name: self.ident.clone(),
977 shim: Ident::new(&shim, Span::call_site()),
978 wasm_bindgen: program.wasm_bindgen.clone(),
979 js_sys: program.js_sys.clone(),
980 string,
981 thread_local,
982 }))
983 }
984}
985
986impl ConvertToAst<(BindgenAttrs, Vec<FnArgAttrs>)> for syn::ItemFn {
987 type Target = ast::Function;
988
989 fn convert(
990 self,
991 (attrs, args_attrs): (BindgenAttrs, Vec<FnArgAttrs>),
992 ) -> Result<Self::Target, Diagnostic> {
993 match self.vis {
994 syn::Visibility::Public(_) => {}
995 _ if attrs.start().is_some() => {}
996 _ => bail_span!(self, "can only #[wasm_bindgen] public functions"),
997 }
998 if self.sig.constness.is_some() {
999 bail_span!(
1000 self.sig.constness,
1001 "can only #[wasm_bindgen] non-const functions"
1002 );
1003 }
1004
1005 let (mut ret, _) = function_from_decl(
1006 &self.sig.ident,
1007 &attrs,
1008 self.sig.clone(),
1009 self.attrs,
1010 self.vis,
1011 FunctionPosition::Free,
1012 Some(args_attrs),
1013 )?;
1014 attrs.check_used();
1015
1016 if is_js_keyword(&ret.name) && ret.name != "default" {
1020 ret.name = format!("_{}", ret.name);
1021 }
1022
1023 Ok(ret)
1024 }
1025}
1026
1027fn get_self_method(r: syn::Receiver) -> ast::MethodSelf {
1029 match &*r.ty {
1036 syn::Type::Reference(ty) => {
1037 if ty.mutability.is_some() {
1038 ast::MethodSelf::RefMutable
1039 } else {
1040 ast::MethodSelf::RefShared
1041 }
1042 }
1043 _ => ast::MethodSelf::ByValue,
1044 }
1045}
1046
1047enum FunctionPosition<'a> {
1048 Extern,
1049 Free,
1050 Impl { self_ty: &'a Ident },
1051}
1052
1053#[allow(clippy::too_many_arguments)]
1055fn function_from_decl(
1056 decl_name: &syn::Ident,
1057 opts: &BindgenAttrs,
1058 sig: syn::Signature,
1059 attrs: Vec<syn::Attribute>,
1060 vis: syn::Visibility,
1061 position: FunctionPosition,
1062 args_attrs: Option<Vec<FnArgAttrs>>,
1063) -> Result<(ast::Function, Option<ast::MethodSelf>), Diagnostic> {
1064 if sig.variadic.is_some() {
1065 bail_span!(sig.variadic, "can't #[wasm_bindgen] variadic functions");
1066 }
1067
1068 if !matches!(position, FunctionPosition::Extern) && !sig.generics.params.is_empty() {
1070 bail_span!(
1071 sig.generics,
1072 "can't #[wasm_bindgen] functions with lifetime or type parameters"
1073 );
1074 }
1075
1076 let syn::Signature { inputs, output, .. } = sig;
1077
1078 let replace_self = |mut t: syn::Type| {
1083 if let FunctionPosition::Impl { self_ty } = position {
1084 struct SelfReplace(Ident);
1089 impl VisitMut for SelfReplace {
1090 fn visit_ident_mut(&mut self, i: &mut proc_macro2::Ident) {
1091 if i == "Self" {
1092 *i = self.0.clone();
1093 }
1094 }
1095 }
1096
1097 let mut replace = SelfReplace(self_ty.clone());
1098 replace.visit_type_mut(&mut t);
1099 }
1100 t
1101 };
1102
1103 let replace_colliding_arg = |i: &mut syn::PatType| {
1106 if let syn::Pat::Ident(ref mut i) = *i.pat {
1107 let ident = i.ident.unraw().to_string();
1108 if is_js_keyword(&ident) {
1112 i.ident = Ident::new(format!("_{ident}").as_str(), i.ident.span());
1113 }
1114 }
1115 };
1116
1117 let mut method_self = None;
1118 let mut arguments = Vec::new();
1119 for arg in inputs.into_iter() {
1120 match arg {
1121 syn::FnArg::Typed(mut c) => {
1122 replace_colliding_arg(&mut c);
1124 *c.ty = replace_self(*c.ty);
1125 arguments.push(c);
1126 }
1127 syn::FnArg::Receiver(r) => {
1128 match position {
1132 FunctionPosition::Free => {
1133 bail_span!(
1134 r.self_token,
1135 "the `self` argument is only allowed for functions in `impl` blocks.\n\n\
1136 If the function is already in an `impl` block, did you perhaps forget to add `#[wasm_bindgen]` to the `impl` block?"
1137 );
1138 }
1139 FunctionPosition::Extern => {
1140 bail_span!(
1141 r.self_token,
1142 "the `self` argument is not allowed for `extern` functions.\n\n\
1143 Did you perhaps mean `this`? For more information on importing JavaScript functions, see:\n\
1144 https://wasm-bindgen.github.io/wasm-bindgen/examples/import-js.html"
1145 );
1146 }
1147 FunctionPosition::Impl { .. } => {}
1148 }
1149
1150 assert!(method_self.is_none());
1153 method_self = Some(get_self_method(r));
1154 }
1155 }
1156 }
1157
1158 let ret_ty_override = opts.unchecked_return_type();
1160 let ret_desc = opts.return_description();
1161 let ret = match output {
1162 syn::ReturnType::Default => None,
1163 syn::ReturnType::Type(_, ty) => Some(ast::FunctionReturnData {
1164 r#type: replace_self(*ty),
1165 js_type: ret_ty_override
1166 .as_ref()
1167 .map_or::<Result<_, Diagnostic>, _>(Ok(None), |(ty, span)| {
1168 check_invalid_type(ty, *span)?;
1169 Ok(Some(ty.to_string()))
1170 })?,
1171 desc: ret_desc.as_ref().map_or::<Result<_, Diagnostic>, _>(
1172 Ok(None),
1173 |(desc, span)| {
1174 check_js_comment_close(desc, *span)?;
1175 Ok(Some(desc.to_string()))
1176 },
1177 )?,
1178 }),
1179 };
1180 if ret.is_none() && (ret_ty_override.is_some() || ret_desc.is_some()) {
1183 if let Some((_, span)) = ret_ty_override {
1184 return Err(Diagnostic::span_error(
1185 span,
1186 "cannot specify return type for a function that doesn't return",
1187 ));
1188 }
1189 if let Some((_, span)) = ret_desc {
1190 return Err(Diagnostic::span_error(
1191 span,
1192 "cannot specify return description for a function that doesn't return",
1193 ));
1194 }
1195 }
1196
1197 let (name, name_span) = if let Some((js_name, js_name_span)) = opts.js_name() {
1198 let kind = operation_kind(opts);
1199 let prefix = match kind {
1200 OperationKind::Setter(_) => "set_",
1201 _ => "",
1202 };
1203 (format!("{prefix}{js_name}"), js_name_span)
1204 } else {
1205 (decl_name.unraw().to_string(), decl_name.span())
1206 };
1207
1208 Ok((
1209 ast::Function {
1210 name_span,
1211 name,
1212 rust_attrs: attrs,
1213 rust_vis: vis,
1214 r#unsafe: sig.unsafety.is_some(),
1215 r#async: sig.asyncness.is_some(),
1216 generate_typescript: opts.skip_typescript().is_none(),
1217 generate_jsdoc: opts.skip_jsdoc().is_none(),
1218 variadic: opts.variadic().is_some(),
1219 ret,
1220 arguments: arguments
1221 .into_iter()
1222 .zip(
1223 args_attrs
1224 .into_iter()
1225 .flatten()
1226 .chain(iter::repeat(FnArgAttrs::default())),
1227 )
1228 .map(|(pat_type, attrs)| ast::FunctionArgumentData {
1229 pat_type,
1230 js_name: attrs.js_name,
1231 js_type: attrs.js_type,
1232 optional: attrs.optional,
1233 desc: attrs.desc,
1234 })
1235 .collect(),
1236 },
1237 method_self,
1238 ))
1239}
1240
1241#[derive(Default, Clone)]
1243struct FnArgAttrs {
1244 js_name: Option<String>,
1245 js_type: Option<String>,
1246 optional: bool,
1247 desc: Option<String>,
1248}
1249
1250fn extract_args_attrs(sig: &mut syn::Signature) -> Result<Vec<FnArgAttrs>, Diagnostic> {
1252 let mut args_attrs = vec![];
1253 let mut seen_optional: Option<Span> = None;
1254 for input in sig.inputs.iter_mut() {
1255 if let syn::FnArg::Typed(pat_type) = input {
1256 let attrs = BindgenAttrs::find(&mut pat_type.attrs)?;
1257
1258 let param_type = attrs.unchecked_param_type();
1260 let optional_param_type = attrs.unchecked_optional_param_type();
1261
1262 if param_type.is_some() && optional_param_type.is_some() {
1263 let mut param_pos_and_span: Option<(usize, Span)> = None;
1265 let mut optional_pos_and_span: Option<(usize, Span)> = None;
1266 for (pos, (_, attr)) in attrs.attrs.iter().enumerate() {
1267 match attr {
1268 BindgenAttr::ParamType(span, _, _) => {
1269 param_pos_and_span = Some((pos, *span));
1270 }
1271 BindgenAttr::OptionalParamType(span, _, _) => {
1272 optional_pos_and_span = Some((pos, *span));
1273 }
1274 _ => {}
1275 }
1276 }
1277 let error_span = match (param_pos_and_span, optional_pos_and_span) {
1279 (Some((p_pos, p_span)), Some((o_pos, o_span))) => {
1280 if p_pos > o_pos {
1281 p_span
1282 } else {
1283 o_span
1284 }
1285 }
1286 (Some((_, p_span)), None) => p_span,
1287 (None, Some((_, o_span))) => o_span,
1288 (None, None) => unreachable!(
1289 "both param_type and optional_param_type are Some, but attrs not found"
1290 ),
1291 };
1292 return Err(Diagnostic::span_error(
1293 error_span,
1294 "cannot use both `unchecked_param_type` and `unchecked_optional_param_type` on the same parameter",
1295 ));
1296 }
1297
1298 let js_type = param_type
1300 .or(optional_param_type)
1301 .map_or::<Result<_, Diagnostic>, _>(Ok(None), |(ty, span)| {
1302 check_invalid_type(ty, span)?;
1303 Ok(Some(ty.to_string()))
1304 })?;
1305
1306 let is_optional = optional_param_type.is_some();
1307
1308 if let Some(optional_span) = seen_optional {
1310 if !is_optional {
1311 return Err(Diagnostic::span_error(
1312 optional_span,
1313 "a required parameter cannot follow an optional parameter",
1314 ));
1315 }
1316 }
1317 if is_optional {
1318 if let Some((_, span)) = optional_param_type {
1319 seen_optional = Some(span);
1320 }
1321 }
1322
1323 let arg_attrs = FnArgAttrs {
1324 js_name: attrs
1325 .js_name()
1326 .map_or(Ok(None), |(js_name_override, span)| {
1327 if is_js_keyword(js_name_override) || !is_valid_ident(js_name_override) {
1328 return Err(Diagnostic::span_error(span, "invalid JS identifier"));
1329 }
1330 Ok(Some(js_name_override.to_string()))
1331 })?,
1332 js_type,
1333 optional: is_optional,
1334 desc: attrs
1335 .param_description()
1336 .map_or::<Result<_, Diagnostic>, _>(Ok(None), |(description, span)| {
1337 check_js_comment_close(description, span)?;
1338 Ok(Some(description.to_string()))
1339 })?,
1340 };
1341 attrs.enforce_used()?;
1343 args_attrs.push(arg_attrs);
1344 }
1345 }
1346 Ok(args_attrs)
1347}
1348
1349pub(crate) trait MacroParse<Ctx> {
1350 fn macro_parse(self, program: &mut ast::Program, context: Ctx) -> Result<(), Diagnostic>;
1355}
1356
1357impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
1358 fn macro_parse(
1359 self,
1360 program: &mut ast::Program,
1361 (opts, tokens): (Option<BindgenAttrs>, &'a mut TokenStream),
1362 ) -> Result<(), Diagnostic> {
1363 match self {
1364 syn::Item::Fn(mut f) => {
1365 let opts = opts.unwrap_or_default();
1366 if let Some(path) = opts.wasm_bindgen() {
1367 program.wasm_bindgen = path.clone();
1368 }
1369 if let Some(path) = opts.js_sys() {
1370 program.js_sys = path.clone();
1371 }
1372 if let Some(path) = opts.wasm_bindgen_futures() {
1373 program.wasm_bindgen_futures = path.clone();
1374 }
1375
1376 if opts.main().is_some() {
1377 opts.check_used();
1378 return main(program, f, tokens);
1379 }
1380
1381 let no_mangle = f
1382 .attrs
1383 .iter()
1384 .enumerate()
1385 .find(|(_, m)| m.path().is_ident("no_mangle"));
1386 if let Some((i, _)) = no_mangle {
1387 f.attrs.remove(i);
1388 }
1389 let args_attrs = extract_args_attrs(&mut f.sig)?;
1391 let comments = extract_doc_comments(&f.attrs);
1392 tokens.extend(quote::quote! { #[allow(dead_code)] });
1396 f.to_tokens(tokens);
1397 if opts.start().is_some() {
1398 if !f.sig.generics.params.is_empty() {
1399 bail_span!(&f.sig.generics, "the start function cannot have generics",);
1400 }
1401 if !f.sig.inputs.is_empty() {
1402 bail_span!(&f.sig.inputs, "the start function cannot have arguments",);
1403 }
1404 }
1405 let method_kind = ast::MethodKind::Operation(ast::Operation {
1406 is_static: true,
1407 kind: operation_kind(&opts),
1408 });
1409 let rust_name = f.sig.ident.clone();
1410 let start = opts.start().is_some();
1411
1412 if opts.this().is_some() && f.sig.inputs.is_empty() {
1413 bail_span!(
1414 &f.sig.inputs,
1415 "functions taking a 'this' argument must have at least one parameter"
1416 );
1417 }
1418
1419 let js_namespace = opts.js_namespace().map(|(ns, _)| ns.0);
1420 program.exports.push(ast::Export {
1421 comments,
1422 function: f.convert((opts, args_attrs))?,
1423 js_class: None,
1424 js_namespace,
1425 method_kind,
1426 method_self: None,
1427 rust_class: None,
1428 rust_name,
1429 start,
1430 wasm_bindgen: program.wasm_bindgen.clone(),
1431 wasm_bindgen_futures: program.wasm_bindgen_futures.clone(),
1432 });
1433 }
1434 syn::Item::Impl(mut i) => {
1435 let opts = opts.unwrap_or_default();
1436 (&mut i).macro_parse(program, opts)?;
1437 i.to_tokens(tokens);
1438 }
1439 syn::Item::ForeignMod(mut f) => {
1440 let opts = match opts {
1441 Some(opts) => opts,
1442 None => BindgenAttrs::find(&mut f.attrs)?,
1443 };
1444 f.macro_parse(program, opts)?;
1445 }
1446 syn::Item::Enum(mut e) => {
1447 let opts = match opts {
1448 Some(opts) => opts,
1449 None => BindgenAttrs::find(&mut e.attrs)?,
1450 };
1451 e.macro_parse(program, (tokens, opts))?;
1452 }
1453 syn::Item::Const(mut c) => {
1454 let opts = match opts {
1455 Some(opts) => opts,
1456 None => BindgenAttrs::find(&mut c.attrs)?,
1457 };
1458 c.macro_parse(program, opts)?;
1459 }
1460 _ => {
1461 bail_span!(
1462 self,
1463 "#[wasm_bindgen] can only be applied to a function, \
1464 struct, enum, impl, or extern block",
1465 );
1466 }
1467 }
1468
1469 Ok(())
1470 }
1471}
1472
1473impl MacroParse<BindgenAttrs> for &mut syn::ItemImpl {
1474 fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
1475 if self.defaultness.is_some() {
1476 bail_span!(
1477 self.defaultness,
1478 "#[wasm_bindgen] default impls are not supported"
1479 );
1480 }
1481 if self.unsafety.is_some() {
1482 bail_span!(
1483 self.unsafety,
1484 "#[wasm_bindgen] unsafe impls are not supported"
1485 );
1486 }
1487 if let Some((_, path, _)) = &self.trait_ {
1488 bail_span!(path, "#[wasm_bindgen] trait impls are not supported");
1489 }
1490 if !self.generics.params.is_empty() {
1491 bail_span!(
1492 self.generics,
1493 "#[wasm_bindgen] generic impls aren't supported"
1494 );
1495 }
1496 let name = match get_ty(&self.self_ty) {
1497 syn::Type::Path(syn::TypePath {
1498 qself: None,
1499 ref path,
1500 }) => path,
1501 _ => bail_span!(
1502 self.self_ty,
1503 "unsupported self type in #[wasm_bindgen] impl"
1504 ),
1505 };
1506 let mut errors = Vec::new();
1507 for item in self.items.iter_mut() {
1508 if let Err(e) = prepare_for_impl_recursion(item, name, program, &opts) {
1509 errors.push(e);
1510 }
1511 }
1512 Diagnostic::from_vec(errors)?;
1513 opts.check_used();
1514 Ok(())
1515 }
1516}
1517
1518fn prepare_for_impl_recursion(
1527 item: &mut syn::ImplItem,
1528 class: &syn::Path,
1529 program: &ast::Program,
1530 impl_opts: &BindgenAttrs,
1531) -> Result<(), Diagnostic> {
1532 let method = match item {
1533 syn::ImplItem::Fn(m) => m,
1534 syn::ImplItem::Const(_) => {
1535 bail_span!(
1536 &*item,
1537 "const definitions aren't supported with #[wasm_bindgen]"
1538 );
1539 }
1540 syn::ImplItem::Type(_) => bail_span!(
1541 &*item,
1542 "type definitions in impls aren't supported with #[wasm_bindgen]"
1543 ),
1544 syn::ImplItem::Macro(_) => {
1545 bail_span!(&*item, "macros in impls aren't supported");
1550 }
1551 syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
1552 other => bail_span!(other, "failed to parse this item as a known item"),
1553 };
1554
1555 let ident = extract_path_ident(class, false)?;
1556
1557 let js_class = impl_opts
1558 .js_class()
1559 .map(|s| s.0.to_string())
1560 .unwrap_or(ident.to_string());
1561
1562 let wasm_bindgen = &program.wasm_bindgen;
1563 let wasm_bindgen_futures = &program.wasm_bindgen_futures;
1564 method.attrs.insert(
1565 0,
1566 syn::Attribute {
1567 pound_token: Default::default(),
1568 style: syn::AttrStyle::Outer,
1569 bracket_token: Default::default(),
1570 meta: syn::parse_quote! { #wasm_bindgen::prelude::__wasm_bindgen_class_marker(#class = #js_class, wasm_bindgen = #wasm_bindgen, wasm_bindgen_futures = #wasm_bindgen_futures) },
1571 },
1572 );
1573
1574 Ok(())
1575}
1576
1577impl MacroParse<&ClassMarker> for &mut syn::ImplItemFn {
1578 fn macro_parse(
1579 self,
1580 program: &mut ast::Program,
1581 ClassMarker {
1582 class,
1583 js_class,
1584 wasm_bindgen,
1585 wasm_bindgen_futures,
1586 }: &ClassMarker,
1587 ) -> Result<(), Diagnostic> {
1588 program.wasm_bindgen = wasm_bindgen.clone();
1589 program.wasm_bindgen_futures = wasm_bindgen_futures.clone();
1590
1591 match self.vis {
1592 syn::Visibility::Public(_) => {}
1593 _ => return Ok(()),
1594 }
1595 if self.defaultness.is_some() {
1596 panic!("default methods are not supported");
1597 }
1598 if self.sig.constness.is_some() {
1599 bail_span!(
1600 self.sig.constness,
1601 "can only #[wasm_bindgen] non-const functions",
1602 );
1603 }
1604
1605 let opts = BindgenAttrs::find(&mut self.attrs)?;
1606
1607 if opts.this().is_some() {
1608 bail_span!(
1609 &self.sig.ident,
1610 "#[wasm_bindgen(this)] cannot be used on impl block methods; \
1611 it is only valid on free functions"
1612 );
1613 }
1614
1615 let comments = extract_doc_comments(&self.attrs);
1616 let args_attrs: Vec<FnArgAttrs> = extract_args_attrs(&mut self.sig)?;
1617 let (function, method_self) = function_from_decl(
1618 &self.sig.ident,
1619 &opts,
1620 self.sig.clone(),
1621 self.attrs.clone(),
1622 self.vis.clone(),
1623 FunctionPosition::Impl { self_ty: class },
1624 Some(args_attrs),
1625 )?;
1626 let method_kind = if opts.constructor().is_some() {
1627 ast::MethodKind::Constructor
1628 } else {
1629 let is_static = method_self.is_none();
1630 let kind = operation_kind(&opts);
1631 ast::MethodKind::Operation(ast::Operation { is_static, kind })
1632 };
1633
1634 if let Some((_, span)) = opts.js_namespace() {
1636 return Err(Diagnostic::span_error(
1637 span[0],
1638 "`js_namespace` cannot be used on methods, getters, setters, or static methods. \
1639 Use `js_namespace` on the exported struct definition instead to put the entire class in a namespace.",
1640 ));
1641 }
1642
1643 program.exports.push(ast::Export {
1644 comments,
1645 function,
1646 js_class: Some(js_class.to_string()),
1647 js_namespace: None,
1648 method_kind,
1649 method_self,
1650 rust_class: Some(class.clone()),
1651 rust_name: self.sig.ident.clone(),
1652 start: false,
1653 wasm_bindgen: program.wasm_bindgen.clone(),
1654 wasm_bindgen_futures: program.wasm_bindgen_futures.clone(),
1655 });
1656 opts.check_used();
1657 Ok(())
1658 }
1659}
1660
1661fn string_enum(
1662 enum_: syn::ItemEnum,
1663 program: &mut ast::Program,
1664 js_name: String,
1665 generate_typescript: bool,
1666 comments: Vec<String>,
1667 js_namespace: Option<Vec<String>>,
1668) -> Result<(), Diagnostic> {
1669 let mut variants = vec![];
1670 let mut variant_values = vec![];
1671
1672 for v in enum_.variants.iter() {
1673 let (_, expr) = match &v.discriminant {
1674 Some(pair) => pair,
1675 None => {
1676 bail_span!(v, "all variants of a string enum must have a string value");
1677 }
1678 };
1679 match get_expr(expr) {
1680 syn::Expr::Lit(syn::ExprLit {
1681 attrs: _,
1682 lit: syn::Lit::Str(str_lit),
1683 }) => {
1684 variants.push(v.ident.clone());
1685 variant_values.push(str_lit.value());
1686 }
1687 expr => bail_span!(
1688 expr,
1689 "enums with #[wasm_bindgen] cannot mix string and non-string values",
1690 ),
1691 }
1692 }
1693
1694 program.imports.push(ast::Import {
1695 module: None,
1696 js_namespace: None,
1697 reexport: None,
1698 kind: ast::ImportKind::Enum(ast::StringEnum {
1699 vis: enum_.vis,
1700 name: enum_.ident,
1701 export_name: js_name,
1702 variants,
1703 variant_values,
1704 comments,
1705 rust_attrs: enum_.attrs,
1706 generate_typescript,
1707 js_namespace,
1708 wasm_bindgen: program.wasm_bindgen.clone(),
1709 }),
1710 });
1711
1712 Ok(())
1713}
1714
1715struct NumericValue<'a> {
1717 negative: bool,
1718 base10_digits: &'a str,
1719}
1720impl<'a> NumericValue<'a> {
1721 fn from_expr(expr: &'a syn::Expr) -> Option<Self> {
1722 match get_expr(expr) {
1723 syn::Expr::Lit(syn::ExprLit {
1724 lit: syn::Lit::Int(int_lit),
1725 ..
1726 }) => Some(Self {
1727 negative: false,
1728 base10_digits: int_lit.base10_digits(),
1729 }),
1730 syn::Expr::Unary(syn::ExprUnary {
1731 op: syn::UnOp::Neg(_),
1732 expr,
1733 ..
1734 }) => Self::from_expr(expr).map(|n| n.neg()),
1735 _ => None,
1736 }
1737 }
1738
1739 fn parse(&self) -> Option<i64> {
1740 let mut value = self.base10_digits.parse::<i64>().ok()?;
1741 if self.negative {
1742 value = -value;
1743 }
1744 Some(value)
1745 }
1746
1747 fn neg(self) -> Self {
1748 Self {
1749 negative: !self.negative,
1750 base10_digits: self.base10_digits,
1751 }
1752 }
1753}
1754
1755impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum {
1756 fn macro_parse(
1757 self,
1758 program: &mut ast::Program,
1759 (tokens, opts): (&'a mut TokenStream, BindgenAttrs),
1760 ) -> Result<(), Diagnostic> {
1761 if self.variants.is_empty() {
1762 bail_span!(self, "cannot export empty enums to JS");
1763 }
1764 for variant in self.variants.iter() {
1765 match variant.fields {
1766 syn::Fields::Unit => (),
1767 _ => bail_span!(
1768 variant.fields,
1769 "enum variants with associated data are not supported with #[wasm_bindgen]"
1770 ),
1771 }
1772 }
1773
1774 let generate_typescript = opts.skip_typescript().is_none();
1775 let private = opts.private().is_some();
1776 let comments = extract_doc_comments(&self.attrs);
1777 let js_name = opts
1778 .js_name()
1779 .map(|s| s.0)
1780 .map_or_else(|| self.ident.to_string(), |s| s.to_string());
1781 if is_js_keyword(&js_name) && js_name != "default" {
1782 bail_span!(
1783 self.ident,
1784 "enum cannot use the JS keyword `{}` as its name",
1785 js_name
1786 );
1787 }
1788
1789 let js_namespace = opts.js_namespace().map(|(ns, _)| ns.0);
1790 opts.check_used();
1791
1792 let is_string_enum = self.variants.iter().any(|v| {
1794 if let Some((_, expr)) = &v.discriminant {
1795 if let syn::Expr::Lit(syn::ExprLit {
1796 lit: syn::Lit::Str(_),
1797 ..
1798 }) = get_expr(expr)
1799 {
1800 return true;
1801 }
1802 }
1803 false
1804 });
1805 if is_string_enum {
1806 return string_enum(
1807 self,
1808 program,
1809 js_name,
1810 generate_typescript,
1811 comments,
1812 js_namespace,
1813 );
1814 }
1815
1816 match self.vis {
1817 syn::Visibility::Public(_) => {}
1818 _ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"),
1819 }
1820
1821 let signed = self.variants.iter().any(|v| match &v.discriminant {
1826 Some((_, expr)) => NumericValue::from_expr(expr).is_some_and(|n| n.negative),
1827 None => false,
1828 });
1829 let underlying_min = if signed { i32::MIN as i64 } else { 0 };
1830 let underlying_max = if signed {
1831 i32::MAX as i64
1832 } else {
1833 u32::MAX as i64
1834 };
1835
1836 let mut last_discriminant: Option<i64> = None;
1837 let mut discriminant_map: HashMap<i64, &syn::Variant> = HashMap::new();
1838
1839 let variants = self
1840 .variants
1841 .iter()
1842 .map(|v| {
1843 let value: i64 = match &v.discriminant {
1844 Some((_, expr)) => match NumericValue::from_expr(expr).and_then(|n| n.parse()) {
1845 Some(value) => value,
1846 _ => bail_span!(
1847 expr,
1848 "C-style enums with #[wasm_bindgen] may only have \
1849 numeric literal values that fit in a 32-bit integer as discriminants. \
1850 Expressions or variables are not supported.",
1851 ),
1852 },
1853 None => {
1854 last_discriminant.map_or(0, |last| last + 1)
1857 }
1858 };
1859
1860 last_discriminant = Some(value);
1861
1862 let underlying = if signed { "i32" } else { "u32" };
1864 let numbers = if signed { "signed numbers" } else { "unsigned numbers" };
1865 if value < underlying_min {
1866 bail_span!(
1867 v,
1868 "C-style enums with #[wasm_bindgen] can only support {0} that can be represented by `{2}`, \
1869 but `{1}` is too small for `{2}`",
1870 numbers,
1871 value,
1872 underlying
1873 );
1874 }
1875 if value > underlying_max {
1876 bail_span!(
1877 v,
1878 "C-style enums with #[wasm_bindgen] can only support {0} that can be represented by `{2}`, \
1879 but `{1}` is too large for `{2}`",
1880 numbers,
1881 value,
1882 underlying
1883 );
1884 }
1885
1886 if let Some(old) = discriminant_map.insert(value, v) {
1888 bail_span!(
1889 v,
1890 "discriminant value `{}` is already used by {} in this enum",
1891 value,
1892 old.ident
1893 );
1894 }
1895
1896 let comments = extract_doc_comments(&v.attrs);
1897 Ok(ast::Variant {
1898 name: v.ident.clone(),
1899 value: value as u32,
1902 comments,
1903 })
1904 })
1905 .collect::<Result<Vec<_>, Diagnostic>>()?;
1906
1907 let hole = (0..=underlying_max)
1910 .find(|v| !discriminant_map.contains_key(v))
1911 .unwrap() as u32;
1912
1913 self.to_tokens(tokens);
1914
1915 program.enums.push(ast::Enum {
1916 rust_name: self.ident,
1917 js_name,
1918 signed,
1919 variants,
1920 comments,
1921 hole,
1922 generate_typescript,
1923 private,
1924 js_namespace,
1925 wasm_bindgen: program.wasm_bindgen.clone(),
1926 });
1927 Ok(())
1928 }
1929}
1930
1931impl MacroParse<BindgenAttrs> for syn::ItemConst {
1932 fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
1933 if opts.typescript_custom_section().is_none() {
1935 bail_span!(self, "#[wasm_bindgen] will not work on constants unless you are defining a #[wasm_bindgen(typescript_custom_section)].");
1936 }
1937
1938 let typescript_custom_section = match get_expr(&self.expr) {
1939 syn::Expr::Lit(syn::ExprLit {
1940 lit: syn::Lit::Str(litstr),
1941 ..
1942 }) => ast::LitOrExpr::Lit(litstr.value()),
1943 expr => ast::LitOrExpr::Expr(expr.clone()),
1944 };
1945
1946 program
1947 .typescript_custom_sections
1948 .push(typescript_custom_section);
1949
1950 opts.check_used();
1951
1952 Ok(())
1953 }
1954}
1955
1956impl MacroParse<BindgenAttrs> for syn::ItemForeignMod {
1957 fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
1958 let mut errors = Vec::new();
1959 if let Some(other) = self.abi.name.filter(|l| l.value() != "C") {
1960 errors.push(err_span!(
1961 other,
1962 "only foreign mods with the `C` ABI are allowed"
1963 ));
1964 }
1965 let js_namespace = opts.js_namespace().map(|(s, _)| s);
1966 let module = module_from_opts(program, &opts)
1967 .map_err(|e| errors.push(e))
1968 .unwrap_or_default();
1969 for item in self.items.into_iter() {
1970 let ctx = ForeignItemCtx {
1971 module: module.clone(),
1972 js_namespace: js_namespace.clone(),
1973 };
1974 if let Err(e) = item.macro_parse(program, ctx) {
1975 errors.push(e);
1976 }
1977 }
1978 Diagnostic::from_vec(errors)?;
1979 opts.check_used();
1980 Ok(())
1981 }
1982}
1983
1984struct ForeignItemCtx {
1985 module: Option<ast::ImportModule>,
1986 js_namespace: Option<JsNamespace>,
1987}
1988
1989impl MacroParse<ForeignItemCtx> for syn::ForeignItem {
1990 fn macro_parse(
1991 mut self,
1992 program: &mut ast::Program,
1993 ctx: ForeignItemCtx,
1994 ) -> Result<(), Diagnostic> {
1995 let item_opts = {
1996 let attrs = match self {
1997 syn::ForeignItem::Fn(ref mut f) => &mut f.attrs,
1998 syn::ForeignItem::Type(ref mut t) => &mut t.attrs,
1999 syn::ForeignItem::Static(ref mut s) => &mut s.attrs,
2000 syn::ForeignItem::Verbatim(v) => {
2001 let mut item: syn::ItemStatic =
2002 syn::parse(v.into()).expect("only foreign functions/types allowed for now");
2003 let item_opts = BindgenAttrs::find(&mut item.attrs)?;
2004 let reexport = item_opts.reexport().cloned();
2005 let kind = item.convert((program, item_opts, &ctx.module))?;
2006
2007 program.imports.push(ast::Import {
2008 module: None,
2009 js_namespace: None,
2010 reexport,
2011 kind,
2012 });
2013
2014 return Ok(());
2015 }
2016 _ => panic!("only foreign functions/types allowed for now"),
2017 };
2018 BindgenAttrs::find(attrs)?
2019 };
2020
2021 let js_namespace = item_opts
2022 .js_namespace()
2023 .map(|(s, _)| s)
2024 .or(ctx.js_namespace)
2025 .map(|s| s.0);
2026 let module = ctx.module;
2027 let reexport = item_opts.reexport().cloned();
2028
2029 let kind = match self {
2030 syn::ForeignItem::Fn(f) => f.convert((program, item_opts, &module))?,
2031 syn::ForeignItem::Type(t) => t.convert((program, item_opts))?,
2032 syn::ForeignItem::Static(s) => s.convert((program, item_opts, &module))?,
2033 _ => panic!("only foreign functions/types allowed for now"),
2034 };
2035
2036 let needs_check = js_namespace.is_none() && module.is_none();
2043 if needs_check {
2044 match &kind {
2045 ast::ImportKind::Function(import_function) => {
2046 if matches!(import_function.kind, ast::ImportFunctionKind::Normal)
2047 && is_non_value_js_keyword(&import_function.function.name)
2048 {
2049 bail_span!(
2050 import_function.rust_name,
2051 "Imported function cannot use the JS keyword `{}` as its name.",
2052 import_function.function.name
2053 );
2054 }
2055 }
2056 ast::ImportKind::Static(import_static) => {
2057 if is_non_value_js_keyword(&import_static.js_name) {
2058 bail_span!(
2059 import_static.rust_name,
2060 "Imported static cannot use the JS keyword `{}` as its name.",
2061 import_static.js_name
2062 );
2063 }
2064 }
2065 ast::ImportKind::String(_) => {
2066 }
2068 ast::ImportKind::Type(import_type) => {
2069 if is_non_value_js_keyword(&import_type.js_name) {
2070 bail_span!(
2071 import_type.rust_name,
2072 "Imported type cannot use the JS keyword `{}` as its name.",
2073 import_type.js_name
2074 );
2075 }
2076 }
2077 ast::ImportKind::Enum(_) => {
2078 }
2080 }
2081 }
2082
2083 program.imports.push(ast::Import {
2084 module,
2085 js_namespace,
2086 reexport,
2087 kind,
2088 });
2089
2090 Ok(())
2091 }
2092}
2093
2094pub fn module_from_opts(
2095 program: &mut ast::Program,
2096 opts: &BindgenAttrs,
2097) -> Result<Option<ast::ImportModule>, Diagnostic> {
2098 if let Some(path) = opts.wasm_bindgen() {
2099 program.wasm_bindgen = path.clone();
2100 }
2101
2102 if let Some(path) = opts.js_sys() {
2103 program.js_sys = path.clone();
2104 }
2105
2106 if let Some(path) = opts.wasm_bindgen_futures() {
2107 program.wasm_bindgen_futures = path.clone();
2108 }
2109
2110 let mut errors = Vec::new();
2111 let module = if let Some((name, span)) = opts.module() {
2112 if opts.inline_js().is_some() {
2113 let msg = "cannot specify both `module` and `inline_js`";
2114 errors.push(Diagnostic::span_error(span, msg));
2115 }
2116 if opts.raw_module().is_some() {
2117 let msg = "cannot specify both `module` and `raw_module`";
2118 errors.push(Diagnostic::span_error(span, msg));
2119 }
2120 Some(ast::ImportModule::Named(name.to_string(), span))
2121 } else if let Some((name, span)) = opts.raw_module() {
2122 if opts.inline_js().is_some() {
2123 let msg = "cannot specify both `raw_module` and `inline_js`";
2124 errors.push(Diagnostic::span_error(span, msg));
2125 }
2126 Some(ast::ImportModule::RawNamed(name.to_string(), span))
2127 } else if let Some((js, _span)) = opts.inline_js() {
2128 let i = program.inline_js.len();
2129 program.inline_js.push(js.to_string());
2130 Some(ast::ImportModule::Inline(i))
2131 } else {
2132 None
2133 };
2134 Diagnostic::from_vec(errors)?;
2135 Ok(module)
2136}
2137
2138fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result<Option<syn::Type>, Diagnostic> {
2140 let t = match ty {
2141 Some(t) => t,
2142 None => return Ok(None),
2143 };
2144 let path = match *get_ty(t) {
2145 syn::Type::Path(syn::TypePath {
2146 qself: None,
2147 ref path,
2148 }) => path,
2149 _ => bail_span!(t, "must be Result<...>"),
2150 };
2151 let seg = path
2152 .segments
2153 .last()
2154 .ok_or_else(|| err_span!(t, "must have at least one segment"))?;
2155 let generics = match seg.arguments {
2156 syn::PathArguments::AngleBracketed(ref t) => t,
2157 _ => bail_span!(t, "must be Result<...>"),
2158 };
2159 let generic = generics
2160 .args
2161 .first()
2162 .ok_or_else(|| err_span!(t, "must have at least one generic parameter"))?;
2163 let ty = match generic {
2164 syn::GenericArgument::Type(t) => t,
2165 other => bail_span!(other, "must be a type parameter"),
2166 };
2167 match get_ty(ty) {
2168 syn::Type::Tuple(t) if t.elems.is_empty() => return Ok(None),
2169 _ => {}
2170 }
2171 Ok(Some(ty.clone()))
2172}
2173
2174fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
2176 attrs
2177 .iter()
2178 .filter_map(|a| {
2179 if a.path().segments.iter().any(|s| s.ident == "doc") {
2182 let tokens = match &a.meta {
2183 syn::Meta::Path(_) => None,
2184 syn::Meta::List(list) => Some(list.tokens.clone()),
2185 syn::Meta::NameValue(name_value) => Some(name_value.value.to_token_stream()),
2186 };
2187
2188 Some(
2189 tokens.into_iter().flatten().filter_map(|t| match t {
2191 TokenTree::Literal(lit) => {
2192 let quoted = lit.to_string();
2193 Some(try_unescape("ed).unwrap_or(quoted))
2194 }
2195 _ => None,
2196 }),
2197 )
2198 } else {
2199 None
2200 }
2201 })
2202 .fold(vec![], |mut acc, a| {
2204 acc.extend(a);
2205 acc
2206 })
2207}
2208
2209fn try_unescape(mut s: &str) -> Option<String> {
2211 s = s.strip_prefix('"').unwrap_or(s);
2212 s = s.strip_suffix('"').unwrap_or(s);
2213 let mut result = String::with_capacity(s.len());
2214 let mut chars = s.chars();
2215 while let Some(c) = chars.next() {
2216 if c == '\\' {
2217 let c = chars.next()?;
2218 match c {
2219 't' => result.push('\t'),
2220 'r' => result.push('\r'),
2221 'n' => result.push('\n'),
2222 '\\' | '\'' | '"' => result.push(c),
2223 'u' => {
2224 if chars.next() != Some('{') {
2225 return None;
2226 }
2227 let (c, next) = unescape_unicode(&mut chars)?;
2228 result.push(c);
2229 if next != '}' {
2230 return None;
2231 }
2232 }
2233 _ => return None,
2234 }
2235 } else {
2236 result.push(c);
2237 }
2238 }
2239 Some(result)
2240}
2241
2242fn unescape_unicode(chars: &mut Chars) -> Option<(char, char)> {
2243 let mut value = 0;
2244 for (i, c) in chars.enumerate() {
2245 match (i, c.to_digit(16)) {
2246 (0..=5, Some(num)) => value = (value << 4) | num,
2247 (1.., None) => return Some((char::from_u32(value)?, c)),
2248 _ => break,
2249 }
2250 }
2251 None
2252}
2253
2254fn extract_path_ident(path: &syn::Path, allow_generics: bool) -> Result<Ident, Diagnostic> {
2257 for segment in path.segments.iter() {
2258 match &segment.arguments {
2259 syn::PathArguments::None => {}
2260 syn::PathArguments::AngleBracketed(_) => {
2261 if !allow_generics {
2262 bail_span!(
2263 path,
2264 "paths with type parameters are not supported in this position"
2265 )
2266 }
2267 }
2268 syn::PathArguments::Parenthesized(_) => {
2269 bail_span!(path, "parenthesized paths are not supported yet")
2270 }
2271 }
2272 }
2273
2274 match path.segments.last() {
2275 Some(value) => Ok(value.ident.clone()),
2276 None => {
2277 bail_span!(path, "empty idents are not supported");
2278 }
2279 }
2280}
2281
2282fn bail_generic_unsupported(span: impl Spanned + ToTokens) -> Result<(), Diagnostic> {
2283 bail_span!(span, "unsupported in wasm-bindgen generics");
2284}
2285
2286fn validate_generic_type_param_bound(bound: &syn::TypeParamBound) -> Result<(), Diagnostic> {
2287 match bound {
2288 syn::TypeParamBound::Trait(trait_bound) => {
2289 if let syn::TraitBoundModifier::Maybe(question) = trait_bound.modifier {
2291 bail_generic_unsupported(question)?;
2292 }
2293 }
2294 syn::TypeParamBound::Lifetime(_) => {
2295 }
2297 syn::TypeParamBound::Verbatim(_) => {}
2298 _ => {}
2299 }
2300 Ok(())
2301}
2302
2303fn validate_generics(generics: &syn::Generics) -> Result<(), Diagnostic> {
2306 if let Some(where_clause) = &generics.where_clause {
2307 for predicate in &where_clause.predicates {
2308 match predicate {
2309 syn::WherePredicate::Type(predicate_type) => {
2310 predicate_type
2312 .bounds
2313 .iter()
2314 .try_for_each(validate_generic_type_param_bound)?;
2315 }
2316 syn::WherePredicate::Lifetime(_) => {
2317 }
2319 _ => bail_generic_unsupported(predicate)?,
2320 }
2321 }
2322 }
2323
2324 for param in &generics.params {
2325 match param {
2326 syn::GenericParam::Lifetime(_) => {
2327 }
2329 syn::GenericParam::Type(type_param) => {
2330 type_param
2331 .bounds
2332 .iter()
2333 .try_for_each(validate_generic_type_param_bound)?;
2334 }
2335 syn::GenericParam::Const(const_param) => bail_generic_unsupported(const_param)?,
2336 }
2337 }
2338
2339 Ok(())
2340}
2341
2342pub fn reset_attrs_used() {
2343 ATTRS.with(|state| {
2344 state.parsed.set(0);
2345 state.checks.set(0);
2346 state.unused_attrs.borrow_mut().clear();
2347 })
2348}
2349
2350pub fn check_unused_attrs(tokens: &mut TokenStream) {
2351 ATTRS.with(|state| {
2352 assert_eq!(state.parsed.get(), state.checks.get());
2353 let unused_attrs = &*state.unused_attrs.borrow();
2354 if !unused_attrs.is_empty() {
2355 let unused_attrs = unused_attrs.iter().map(|UnusedState { error, ident }| {
2356 if *error {
2357 let text = format!("invalid attribute {ident} in this position");
2358 quote::quote_spanned! { ident.span() => ::core::compile_error!(#text); }
2359 } else {
2360 quote::quote! { let #ident: (); }
2361 }
2362 });
2363 tokens.extend(quote::quote! {
2364 const _: () = {
2366 #(#unused_attrs)*
2367 };
2368 });
2369 }
2370 })
2371}
2372
2373fn operation_kind(opts: &BindgenAttrs) -> ast::OperationKind {
2374 let mut operation_kind = ast::OperationKind::Regular;
2375 if opts.this().is_some() {
2376 operation_kind = ast::OperationKind::RegularThis;
2377 }
2378 if let Some(g) = opts.getter() {
2379 operation_kind = ast::OperationKind::Getter(g.clone());
2380 }
2381 if let Some(s) = opts.setter() {
2382 operation_kind = ast::OperationKind::Setter(s.clone());
2383 }
2384 if opts.indexing_getter().is_some() {
2385 operation_kind = ast::OperationKind::IndexingGetter;
2386 }
2387 if opts.indexing_setter().is_some() {
2388 operation_kind = ast::OperationKind::IndexingSetter;
2389 }
2390 if opts.indexing_deleter().is_some() {
2391 operation_kind = ast::OperationKind::IndexingDeleter;
2392 }
2393 operation_kind
2394}
2395
2396pub fn link_to(opts: BindgenAttrs) -> Result<ast::LinkToModule, Diagnostic> {
2397 let mut program = ast::Program::default();
2398 let module = module_from_opts(&mut program, &opts)?.ok_or_else(|| {
2399 Diagnostic::span_error(Span::call_site(), "`link_to!` requires a module.")
2400 })?;
2401 if let ast::ImportModule::Named(p, s) | ast::ImportModule::RawNamed(p, s) = &module {
2402 if !p.starts_with("./") && !p.starts_with("../") && !p.starts_with('/') {
2403 return Err(Diagnostic::span_error(
2404 *s,
2405 "`link_to!` does not support module paths.",
2406 ));
2407 }
2408 }
2409 opts.enforce_used()?;
2410 program.linked_modules.push(module);
2411 Ok(ast::LinkToModule(program))
2412}
2413
2414fn main(program: &ast::Program, mut f: ItemFn, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
2415 if f.sig.ident != "main" {
2416 bail_span!(&f.sig.ident, "the main function has to be called main");
2417 }
2418 if let Some(constness) = f.sig.constness {
2419 bail_span!(&constness, "the main function cannot be const");
2420 }
2421 if !f.sig.generics.params.is_empty() {
2422 bail_span!(&f.sig.generics, "the main function cannot have generics");
2423 }
2424 if !f.sig.inputs.is_empty() {
2425 bail_span!(&f.sig.inputs, "the main function cannot have arguments");
2426 }
2427
2428 let r#return = f.sig.output;
2429 f.sig.output = ReturnType::Default;
2430 let body = f.block.as_ref();
2431
2432 let wasm_bindgen = &program.wasm_bindgen;
2433 let wasm_bindgen_futures = &program.wasm_bindgen_futures;
2434
2435 if f.sig.asyncness.take().is_some() {
2436 *f.block = syn::parse2(quote::quote! {
2437 {
2438 async fn __wasm_bindgen_generated_main() #r#return #body
2439 #wasm_bindgen_futures::spawn_local(
2440 async move {
2441 use #wasm_bindgen::__rt::Main;
2442 let __ret = __wasm_bindgen_generated_main();
2443 (&mut &mut &mut #wasm_bindgen::__rt::MainWrapper(Some(__ret.await))).__wasm_bindgen_main()
2444 },
2445 )
2446 }
2447 })
2448 .unwrap();
2449 } else {
2450 *f.block = syn::parse2(quote::quote! {
2451 {
2452 fn __wasm_bindgen_generated_main() #r#return #body
2453 use #wasm_bindgen::__rt::Main;
2454 let __ret = __wasm_bindgen_generated_main();
2455 (&mut &mut &mut #wasm_bindgen::__rt::MainWrapper(Some(__ret))).__wasm_bindgen_main()
2456 }
2457 })
2458 .unwrap();
2459 }
2460
2461 f.to_tokens(tokens);
2462
2463 Ok(())
2464}
2465
2466#[cfg(test)]
2467mod tests {
2468 #[test]
2469 fn test_try_unescape() {
2470 use super::try_unescape;
2471 assert_eq!(try_unescape("hello").unwrap(), "hello");
2472 assert_eq!(try_unescape("\"hello").unwrap(), "hello");
2473 assert_eq!(try_unescape("hello\"").unwrap(), "hello");
2474 assert_eq!(try_unescape("\"hello\"").unwrap(), "hello");
2475 assert_eq!(try_unescape("hello\\\\").unwrap(), "hello\\");
2476 assert_eq!(try_unescape("hello\\n").unwrap(), "hello\n");
2477 assert_eq!(try_unescape("hello\\u"), None);
2478 assert_eq!(try_unescape("hello\\u{"), None);
2479 assert_eq!(try_unescape("hello\\u{}"), None);
2480 assert_eq!(try_unescape("hello\\u{0}").unwrap(), "hello\0");
2481 assert_eq!(try_unescape("hello\\u{000000}").unwrap(), "hello\0");
2482 assert_eq!(try_unescape("hello\\u{0000000}"), None);
2483 }
2484}