diff --git a/crates/backend/src/error.rs b/crates/backend/src/error.rs index b286c922..13eacb2f 100644 --- a/crates/backend/src/error.rs +++ b/crates/backend/src/error.rs @@ -1,6 +1,20 @@ use proc_macro2::*; use quote::ToTokens; +#[macro_export] +macro_rules! err_span { + ($span:expr, $($msg:tt)*) => ( + ::backend::Diagnostic::span_error(&$span, format!($($msg)*)) + ) +} + +#[macro_export] +macro_rules! bail_span { + ($($t:tt)*) => ( + return Err(err_span!($($t)*).into()) + ) +} + pub struct Diagnostic { inner: Repr, } diff --git a/crates/macro-support/src/lib.rs b/crates/macro-support/src/lib.rs old mode 100755 new mode 100644 index 9ec85916..cb777337 --- a/crates/macro-support/src/lib.rs +++ b/crates/macro-support/src/lib.rs @@ -7,6 +7,7 @@ extern crate proc_macro2; extern crate quote; #[macro_use] extern crate syn; +#[macro_use] extern crate wasm_bindgen_backend as backend; extern crate wasm_bindgen_shared as shared; diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 7f353dc9..7d8a8bcb 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1,7 +1,7 @@ use backend::ast; use backend::Diagnostic; use backend::util::{ident_ty, ShortHash}; -use proc_macro2::{Ident, Span, TokenStream, TokenTree}; +use proc_macro2::{Ident, Span, TokenStream, TokenTree, Delimiter}; use quote::ToTokens; use shared; use syn; @@ -16,7 +16,7 @@ pub struct BindgenAttrs { impl BindgenAttrs { /// Find and parse the wasm_bindgen attributes. - fn find(attrs: &mut Vec) -> BindgenAttrs { + fn find(attrs: &mut Vec) -> Result { let pos = attrs .iter() .enumerate() @@ -24,18 +24,22 @@ impl BindgenAttrs { .map(|a| a.0); let pos = match pos { Some(i) => i, - None => return BindgenAttrs::default(), + None => return Ok(BindgenAttrs::default()), }; - let mut tts = attrs.remove(pos).tts.into_iter(); - let tt = match tts.next() { - Some(TokenTree::Group(d)) => d.stream(), - Some(_) => panic!("malformed #[wasm_bindgen] attribute"), - None => return BindgenAttrs::default(), + let attr = attrs.remove(pos); + let mut tts = attr.tts.clone().into_iter(); + let group = match tts.next() { + Some(TokenTree::Group(d)) => d, + Some(_) => bail_span!(attr, "malformed #[wasm_bindgen] attribute"), + None => return Ok(BindgenAttrs::default()), }; if tts.next().is_some() { - panic!("malformed #[wasm_bindgen] attribute"); + bail_span!(attr, "malformed #[wasm_bindgen] attribute"); } - syn::parse(tt.into()).expect("malformed #[wasm_bindgen] attribute") + if group.delimiter() != Delimiter::Parenthesis { + bail_span!(attr, "malformed #[wasm_bindgen] attribute"); + } + super::syn_parse(group.stream(), "#[wasm_bindgen] attribute options") } /// Get the first module attribute @@ -304,15 +308,16 @@ trait ConvertToAst { /// Convert into our target. /// /// Since this is used in a procedural macro, use panic to fail. - fn convert(self, context: Ctx) -> Self::Target; + fn convert(self, context: Ctx) -> Result; } impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct { type Target = ast::Struct; - fn convert(self, (): ()) -> Self::Target { + fn convert(self, (): ()) -> Result { if self.generics.params.len() > 0 { - panic!( + bail_span!( + self.generics, "structs with #[wasm_bindgen] cannot have lifetime or \ type parameters currently" ); @@ -332,7 +337,7 @@ impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct { let name_str = name.to_string(); let getter = shared::struct_field_get(&ident, &name_str); let setter = shared::struct_field_set(&ident, &name_str); - let opts = BindgenAttrs::find(&mut field.attrs); + let opts = BindgenAttrs::find(&mut field.attrs)?; let comments = extract_doc_comments(&field.attrs); fields.push(ast::StructField { name: name.clone(), @@ -346,20 +351,28 @@ impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct { } } let comments: Vec = extract_doc_comments(&self.attrs); - ast::Struct { + Ok(ast::Struct { name: self.ident.clone(), fields, comments, - } + }) } } impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn { type Target = ast::ImportKind; - fn convert(self, (opts, module): (BindgenAttrs, &'a Option)) -> Self::Target { + fn convert(self, (opts, module): (BindgenAttrs, &'a Option)) + -> Result + { let js_name = opts.js_name().unwrap_or(&self.ident).clone(); - let wasm = function_from_decl(&js_name, self.decl, self.attrs, self.vis, false).0; + let wasm = function_from_decl( + &js_name, + self.decl.clone(), + self.attrs.clone(), + self.vis.clone(), + false, + )?.0; let catch = opts.catch(); let js_ret = if catch { // TODO: this assumes a whole bunch: @@ -369,8 +382,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn // * The actual type is the first type parameter // // should probably fix this one day... - extract_first_ty_param(wasm.ret.as_ref()) - .expect("can't `catch` without returning a Result") + extract_first_ty_param(wasm.ret.as_ref())? } else { wasm.ret.clone() }; @@ -387,24 +399,27 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn let class = wasm .arguments .get(0) - .expect("methods must have at least one argument"); + .ok_or_else(|| { + err_span!(self, "imported methods must have at least one argument") + })?; let class = match class.ty { syn::Type::Reference(syn::TypeReference { mutability: None, ref elem, .. }) => &**elem, - _ => panic!("first argument of method must be a shared reference"), + _ => { + bail_span!(class.ty, "first argument of method must be a shared reference") + } }; let class_name = match *class { syn::Type::Path(syn::TypePath { qself: None, ref path, }) => path, - _ => panic!("first argument of method must be a path"), + _ => bail_span!(class, "first argument of method must be a path"), }; - let class_name = extract_path_ident(class_name) - .expect("first argument of method must be a bare type"); + let class_name = extract_path_ident(class_name)?; let class_name = opts .js_class() .map(Into::into) @@ -433,17 +448,16 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn } else if opts.constructor() { let class = match wasm.ret { Some(ref ty) => ty, - _ => panic!("constructor returns must be bare types"), + _ => bail_span!(self, "constructor returns must be bare types"), }; let class_name = match *class { syn::Type::Path(syn::TypePath { qself: None, ref path, }) => path, - _ => panic!("first argument of method must be a path"), + _ => bail_span!(self, "return value of constructor must be a bare path"), }; - let class_name = extract_path_ident(class_name) - .expect("first argument of method must be a bare type"); + let class_name = extract_path_ident(class_name)?; ast::ImportFunctionKind::Method { class: class_name.to_string(), @@ -462,7 +476,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn let data = (ns, &self.ident, module); format!("__wbg_{}_{}", js_name, ShortHash(data)) }; - ast::ImportKind::Function(ast::ImportFunction { + Ok(ast::ImportKind::Function(ast::ImportFunction { function: wasm, kind, js_ret, @@ -471,59 +485,59 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn rust_name: self.ident.clone(), shim: Ident::new(&shim, Span::call_site()), doc_comment: None, - }) + })) } } impl ConvertToAst<()> for syn::ForeignItemType { type Target = ast::ImportKind; - fn convert(self, (): ()) -> Self::Target { - ast::ImportKind::Type(ast::ImportType { + fn convert(self, (): ()) -> Result { + Ok(ast::ImportKind::Type(ast::ImportType { vis: self.vis, name: self.ident, attrs: self.attrs, doc_comment: None, - }) + })) } } impl ConvertToAst for syn::ForeignItemStatic { type Target = ast::ImportKind; - fn convert(self, opts: BindgenAttrs) -> Self::Target { + fn convert(self, opts: BindgenAttrs) -> Result { if self.mutability.is_some() { - panic!("cannot import mutable globals yet") + bail_span!(self.mutability, "cannot import mutable globals yet") } let js_name = opts.js_name().unwrap_or(&self.ident); let shim = format!("__wbg_static_accessor_{}_{}", js_name, self.ident); - ast::ImportKind::Static(ast::ImportStatic { + Ok(ast::ImportKind::Static(ast::ImportStatic { ty: *self.ty, vis: self.vis, rust_name: self.ident.clone(), js_name: js_name.clone(), shim: Ident::new(&shim, Span::call_site()), - }) + })) } } impl ConvertToAst for syn::ItemFn { type Target = ast::Function; - fn convert(self, attrs: BindgenAttrs) -> Self::Target { + fn convert(self, attrs: BindgenAttrs) -> Result { match self.vis { syn::Visibility::Public(_) => {} - _ => panic!("can only bindgen public functions"), + _ => bail_span!(self, "can only #[wasm_bindgen] public functions"), } if self.constness.is_some() { - panic!("can only bindgen non-const functions"); + bail_span!(self.constness, "can only #[wasm_bindgen] non-const functions"); } if self.unsafety.is_some() { - panic!("can only bindgen safe functions"); + bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions"); } let name = attrs.js_name().unwrap_or(&self.ident); - function_from_decl(name, self.decl, self.attrs, self.vis, false).0 + Ok(function_from_decl(name, self.decl, self.attrs, self.vis, false)?.0) } } @@ -534,15 +548,18 @@ fn function_from_decl( attrs: Vec, vis: syn::Visibility, allow_self: bool, -) -> (ast::Function, Option) { +) -> Result<(ast::Function, Option), Diagnostic> { if decl.variadic.is_some() { - panic!("can't bindgen variadic functions") + bail_span!(decl.variadic, "can't #[wasm_bindgen] variadic functions"); } if decl.generics.params.len() > 0 { - panic!("can't bindgen functions with lifetime or type parameters") + bail_span!( + decl.generics, + "can't #[wasm_bindgen] functions with lifetime or type parameters", + ); } - assert_no_lifetimes(&mut decl); + assert_no_lifetimes(&mut decl)?; let syn::FnDecl { inputs, output, .. } = { *decl }; @@ -574,7 +591,7 @@ fn function_from_decl( syn::ReturnType::Type(_, ty) => Some(*ty), }; - ( + Ok(( ast::Function { name: name.clone(), arguments, @@ -583,7 +600,7 @@ fn function_from_decl( rust_attrs: attrs, }, method_self, - ) + )) } pub(crate) trait MacroParse { @@ -623,11 +640,11 @@ impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { constructor: None, comments, rust_name: f.ident.clone(), - function: f.convert(opts.unwrap_or_default()), + function: f.convert(opts.unwrap_or_default())?, }); } syn::Item::Struct(mut s) => { - program.structs.push((&mut s).convert(())); + program.structs.push((&mut s).convert(())?); s.to_tokens(tokens); } syn::Item::Impl(mut i) => { @@ -635,17 +652,23 @@ impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { i.to_tokens(tokens); } syn::Item::ForeignMod(mut f) => { - let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs)); + let opts = match opts { + Some(opts) => opts, + None => BindgenAttrs::find(&mut f.attrs)?, + }; f.macro_parse(program, opts)?; } syn::Item::Enum(e) => { e.to_tokens(tokens); e.macro_parse(program, ())?; } - _ => panic!( - "#[wasm_bindgen] can only be applied to a function, \ - struct, enum, impl, or extern block" - ), + _ => { + bail_span!( + self, + "#[wasm_bindgen] can only be applied to a function, \ + struct, enum, impl, or extern block" + ) + } } Ok(()) @@ -657,26 +680,23 @@ impl<'a> MacroParse<()> for &'a mut syn::ItemImpl { -> Result<(), Diagnostic> { if self.defaultness.is_some() { - panic!("default impls are not supported"); + bail_span!(self.defaultness, "#[wasm_bindgen] default impls are not supported"); } if self.unsafety.is_some() { - panic!("unsafe impls are not supported"); + bail_span!(self.unsafety, "#[wasm_bindgen] unsafe impls are not supported"); } - if self.trait_.is_some() { - panic!("trait impls are not supported"); + if let Some((_, path, _)) = &self.trait_ { + bail_span!(path, "#[wasm_bindgen] trait impls are not supported"); } if self.generics.params.len() > 0 { - panic!("generic impls aren't supported"); + bail_span!(self.generics, "#[wasm_bindgen] generic impls aren't supported"); } let name = match *self.self_ty { syn::Type::Path(syn::TypePath { qself: None, ref path, - }) => match extract_path_ident(path) { - Some(ident) => ident, - None => panic!("unsupported self type in impl"), - }, - _ => panic!("unsupported self type in impl"), + }) => extract_path_ident(path)?, + _ => bail_span!(self.self_ty, "unsupported self type in #[wasm_bindgen] impl"), }; let mut errors = Vec::new(); for item in self.items.iter_mut() { @@ -695,10 +715,16 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { let (class, item) = self; replace_self(class, item); let method = match item { - syn::ImplItem::Const(_) => panic!("const definitions aren't supported"), - syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"), syn::ImplItem::Method(ref mut m) => m, - syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"), + syn::ImplItem::Const(_) => { + bail_span!(&*item, "const definitions aren't supported with #[wasm_bindgen]"); + } + syn::ImplItem::Type(_) => { + bail_span!(&*item, "type definitions in impls aren't supported with #[wasm_bindgen]") + } + syn::ImplItem::Macro(_) => { + bail_span!(&*item, "macros in impls aren't supported"); + } syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"), }; match method.vis { @@ -709,13 +735,19 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { panic!("default methods are not supported"); } if method.sig.constness.is_some() { - panic!("can only bindgen non-const functions"); + bail_span!( + method.sig.constness, + "can only #[wasm_bindgen] non-const functions", + ); } if method.sig.unsafety.is_some() { - panic!("can only bindgen safe functions"); + bail_span!( + method.sig.unsafety, + "can only bindgen safe functions", + ); } - let opts = BindgenAttrs::find(&mut method.attrs); + let opts = BindgenAttrs::find(&mut method.attrs)?; let comments = extract_doc_comments(&method.attrs); let is_constructor = opts.constructor(); let constructor = if is_constructor { @@ -730,7 +762,7 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { method.attrs.clone(), method.vis.clone(), true, - ); + )?; program.exports.push(ast::Export { class: Some(class.clone()), @@ -750,7 +782,7 @@ impl MacroParse<()> for syn::ItemEnum { { match self.vis { syn::Visibility::Public(_) => {} - _ => panic!("only public enums are allowed"), + _ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"), } let variants = self @@ -760,7 +792,7 @@ impl MacroParse<()> for syn::ItemEnum { .map(|(i, v)| { match v.fields { syn::Fields::Unit => (), - _ => panic!("Only C-Style enums allowed"), + _ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"), } let value = match v.discriminant { Some(( @@ -771,20 +803,30 @@ impl MacroParse<()> for syn::ItemEnum { }), )) => { if int_lit.value() > ::max_value() as u64 { - panic!("Enums can only support numbers that can be represented as u32"); + bail_span!( + int_lit, + "enums with #[wasm_bindgen] can only support \ + numbers that can be represented as u32" + ); } int_lit.value() as u32 } None => i as u32, - _ => panic!("Enums may only have number literal values"), + Some((_, ref expr)) => { + bail_span!( + expr, + "enums with #[wasm_bidngen] may only have \ + number literal values", + ) + } }; - ast::Variant { + Ok(ast::Variant { name: v.ident.clone(), value, - } + }) }) - .collect(); + .collect::>()?; let comments = extract_doc_comments(&self.attrs); program.enums.push(ast::Enum { name: self.ident, @@ -799,47 +841,62 @@ impl MacroParse for syn::ItemForeignMod { fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> { + let mut errors = Vec::new(); match self.abi.name { Some(ref l) if l.value() == "C" => {} None => {} - _ => panic!("only foreign mods with the `C` ABI are allowed"), + Some(ref other) => { + errors.push(err_span!(other, "only foreign mods with the `C` ABI are allowed")); + } } for mut item in self.items.into_iter() { - let item_opts = { - let attrs = match item { - syn::ForeignItem::Fn(ref mut f) => &mut f.attrs, - syn::ForeignItem::Type(ref mut t) => &mut t.attrs, - syn::ForeignItem::Static(ref mut s) => &mut s.attrs, - _ => panic!("only foreign functions/types allowed for now"), - }; - BindgenAttrs::find(attrs) - }; - let module = item_opts.module().or(opts.module()).map(|s| s.to_string()); - let version = item_opts - .version() - .or(opts.version()) - .map(|s| s.to_string()); - let js_namespace = item_opts.js_namespace().or(opts.js_namespace()).cloned(); - let mut kind = match item { - syn::ForeignItem::Fn(f) => f.convert((item_opts, &module)), - syn::ForeignItem::Type(t) => t.convert(()), - syn::ForeignItem::Static(s) => s.convert(item_opts), + if let Err(e) = item.macro_parse(program, &opts) { + errors.push(e); + } + } + Diagnostic::from_vec(errors) + } +} + +impl<'a> MacroParse<&'a BindgenAttrs> for syn::ForeignItem { + fn macro_parse(mut self, program: &mut ast::Program, opts: &'a BindgenAttrs) + -> Result<(), Diagnostic> + { + let item_opts = { + let attrs = match self { + syn::ForeignItem::Fn(ref mut f) => &mut f.attrs, + syn::ForeignItem::Type(ref mut t) => &mut t.attrs, + syn::ForeignItem::Static(ref mut s) => &mut s.attrs, _ => panic!("only foreign functions/types allowed for now"), }; + BindgenAttrs::find(attrs)? + }; + let module = item_opts.module().or(opts.module()).map(|s| s.to_string()); + let version = item_opts + .version() + .or(opts.version()) + .map(|s| s.to_string()); + let js_namespace = item_opts.js_namespace().or(opts.js_namespace()).cloned(); + let kind = match self { + syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?, + syn::ForeignItem::Type(t) => t.convert(())?, + syn::ForeignItem::Static(s) => s.convert(item_opts)?, + _ => panic!("only foreign functions/types allowed for now"), + }; + + program.imports.push(ast::Import { + module, + version, + js_namespace, + kind, + }); - program.imports.push(ast::Import { - module, - version, - js_namespace, - kind, - }); - } Ok(()) } } /// Get the first type parameter of a generic type, errors on incorrect input. -fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result, ()> { +fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result, Diagnostic> { let t = match ty { Some(t) => t, None => return Ok(None), @@ -849,16 +906,21 @@ fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result, ( qself: None, ref path, }) => path, - _ => return Err(()), + _ => bail_span!(t, "must be Result<...>"), }; - let seg = path.segments.last().ok_or(())?.into_value(); + let seg = path.segments.last() + .ok_or_else(|| err_span!(t, "must have at least one segment"))? + .into_value(); let generics = match seg.arguments { syn::PathArguments::AngleBracketed(ref t) => t, - _ => return Err(()), + _ => bail_span!(t, "must be Result<...>"), }; - let ty = match *generics.args.first().ok_or(())?.into_value() { - syn::GenericArgument::Type(ref t) => t, - _ => return Err(()), + let generic = generics.args.first() + .ok_or_else(|| err_span!(t, "must have at least one generic parameter"))? + .into_value(); + let ty = match generic { + syn::GenericArgument::Type(t) => t, + other => bail_span!(other, "must be a type parameter"), }; match *ty { syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Ok(None), @@ -910,32 +972,37 @@ fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec { } /// Check there are no lifetimes on the function. -fn assert_no_lifetimes(decl: &mut syn::FnDecl) { - struct Walk; - - impl<'ast> syn::visit_mut::VisitMut for Walk { - fn visit_lifetime_mut(&mut self, _i: &mut syn::Lifetime) { - panic!( - "it is currently not sound to use lifetimes in function \ - signatures" - ); - } +fn assert_no_lifetimes(decl: &mut syn::FnDecl) -> Result<(), Diagnostic> { + struct Walk { + diagnostics: Vec, } - syn::visit_mut::VisitMut::visit_fn_decl_mut(&mut Walk, decl); + impl<'ast> syn::visit_mut::VisitMut for Walk { + fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) { + self.diagnostics.push(err_span!( + &*i, + "it is currently not sound to use lifetimes in function \ + signatures" + )); + } + } + let mut walk = Walk { diagnostics: Vec::new() }; + syn::visit_mut::VisitMut::visit_fn_decl_mut(&mut walk, decl); + Diagnostic::from_vec(walk.diagnostics) } /// If the path is a single ident, return it. -fn extract_path_ident(path: &syn::Path) -> Option { +fn extract_path_ident(path: &syn::Path) -> Result { if path.leading_colon.is_some() { - return None; + bail_span!(path, "global paths are not supported yet"); } if path.segments.len() != 1 { - return None; + bail_span!(path, "multi-segment paths are not supported yet"); } - match path.segments.first().unwrap().value().arguments { + let value = &path.segments[0]; + match value.arguments { syn::PathArguments::None => {} - _ => return None, + _ => bail_span!(path, "paths with type parameters are not supported yet"), } - path.segments.first().map(|v| v.value().ident.clone()) + Ok(value.ident.clone()) } diff --git a/crates/macro/ui-tests/invalid-attr.rs b/crates/macro/ui-tests/invalid-attr.rs new file mode 100644 index 00000000..23da4263 --- /dev/null +++ b/crates/macro/ui-tests/invalid-attr.rs @@ -0,0 +1,23 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(x)] +pub fn foo() {} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(y)] + fn bar(); + + #[wasm_bindgen z] + fn bar(); + + #[wasm_bindgen(z2) x] + fn bar(); + + #[wasm_bindgen { }] + fn bar(); +} diff --git a/crates/macro/ui-tests/invalid-attr.stderr b/crates/macro/ui-tests/invalid-attr.stderr new file mode 100644 index 00000000..faec4976 --- /dev/null +++ b/crates/macro/ui-tests/invalid-attr.stderr @@ -0,0 +1,32 @@ +error: error parsing #[wasm_bindgen] attribute options: failed to parse anything + --> $DIR/invalid-attr.rs:7:16 + | +7 | #[wasm_bindgen(x)] + | ^ + +error: error parsing #[wasm_bindgen] attribute options: failed to parse anything + --> $DIR/invalid-attr.rs:12:20 + | +12 | #[wasm_bindgen(y)] + | ^ + +error: malformed #[wasm_bindgen] attribute + --> $DIR/invalid-attr.rs:15:5 + | +15 | #[wasm_bindgen z] + | ^^^^^^^^^^^^^^^^^ + +error: malformed #[wasm_bindgen] attribute + --> $DIR/invalid-attr.rs:18:5 + | +18 | #[wasm_bindgen(z2) x] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: malformed #[wasm_bindgen] attribute + --> $DIR/invalid-attr.rs:21:5 + | +21 | #[wasm_bindgen { }] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/crates/macro/ui-tests/invalid-enums.rs b/crates/macro/ui-tests/invalid-enums.rs new file mode 100644 index 00000000..44c983c0 --- /dev/null +++ b/crates/macro/ui-tests/invalid-enums.rs @@ -0,0 +1,23 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +enum A {} + +#[wasm_bindgen] +pub enum B { + D(u32), +} + +#[wasm_bindgen] +pub enum C { + X = 1 + 3, +} + +#[wasm_bindgen] +pub enum D { + X = 4294967296, +} diff --git a/crates/macro/ui-tests/invalid-enums.stderr b/crates/macro/ui-tests/invalid-enums.stderr new file mode 100644 index 00000000..06da7570 --- /dev/null +++ b/crates/macro/ui-tests/invalid-enums.stderr @@ -0,0 +1,26 @@ +error: only public enums are allowed with #[wasm_bindgen] + --> $DIR/invalid-enums.rs:8:1 + | +8 | enum A {} + | ^^^^^^^^^ + +error: only C-Style enums allowed with #[wasm_bindgen] + --> $DIR/invalid-enums.rs:12:6 + | +12 | D(u32), + | ^^^^^ + +error: enums with #[wasm_bidngen] may only have number literal values + --> $DIR/invalid-enums.rs:17:9 + | +17 | X = 1 + 3, + | ^^^^^ + +error: enums with #[wasm_bindgen] can only support numbers that can be represented as u32 + --> $DIR/invalid-enums.rs:22:9 + | +22 | X = 4294967296, + | ^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/crates/macro/ui-tests/invalid-imports.rs b/crates/macro/ui-tests/invalid-imports.rs new file mode 100644 index 00000000..9ed8d84a --- /dev/null +++ b/crates/macro/ui-tests/invalid-imports.rs @@ -0,0 +1,43 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + type A; + + fn f() -> &'static u32; + + #[wasm_bindgen(method)] + fn f1(); + #[wasm_bindgen(method)] + fn f2(x: u32); + #[wasm_bindgen(method)] + fn f3(x: &&u32); + #[wasm_bindgen(method)] + fn f4(x: &foo::Bar); + #[wasm_bindgen(method)] + fn f4(x: &::Bar); + #[wasm_bindgen(method)] + fn f4(x: &Bar); + #[wasm_bindgen(method)] + fn f4(x: &Fn(T)); + + #[wasm_bindgen(constructor)] + fn f(); + #[wasm_bindgen(constructor)] + fn f() -> ::Bar; + #[wasm_bindgen(constructor)] + fn f() -> &Bar; + + #[wasm_bindgen(catch)] + fn f() -> u32; + #[wasm_bindgen(catch)] + fn f() -> &u32; + #[wasm_bindgen(catch)] + fn f() -> Result<>; + #[wasm_bindgen(catch)] + fn f() -> Result<'a>; +} diff --git a/crates/macro/ui-tests/invalid-imports.stderr b/crates/macro/ui-tests/invalid-imports.stderr new file mode 100644 index 00000000..f2574c45 --- /dev/null +++ b/crates/macro/ui-tests/invalid-imports.stderr @@ -0,0 +1,92 @@ +error: it is currently not sound to use lifetimes in function signatures + --> $DIR/invalid-imports.rs:11:16 + | +11 | fn f() -> &'static u32; + | ^^^^^^^ + +error: imported methods must have at least one argument + --> $DIR/invalid-imports.rs:14:5 + | +14 | fn f1(); + | ^^^^^^^^ + +error: first argument of method must be a shared reference + --> $DIR/invalid-imports.rs:16:14 + | +16 | fn f2(x: u32); + | ^^^ + +error: first argument of method must be a path + --> $DIR/invalid-imports.rs:18:14 + | +18 | fn f3(x: &&u32); + | ^^^^^ + +error: multi-segment paths are not supported yet + --> $DIR/invalid-imports.rs:20:15 + | +20 | fn f4(x: &foo::Bar); + | ^^^^^^^^ + +error: global paths are not supported yet + --> $DIR/invalid-imports.rs:22:15 + | +22 | fn f4(x: &::Bar); + | ^^^^^ + +error: paths with type parameters are not supported yet + --> $DIR/invalid-imports.rs:24:15 + | +24 | fn f4(x: &Bar); + | ^^^^^^ + +error: paths with type parameters are not supported yet + --> $DIR/invalid-imports.rs:26:15 + | +26 | fn f4(x: &Fn(T)); + | ^^^^^ + +error: constructor returns must be bare types + --> $DIR/invalid-imports.rs:29:5 + | +29 | fn f(); + | ^^^^^^^ + +error: global paths are not supported yet + --> $DIR/invalid-imports.rs:31:15 + | +31 | fn f() -> ::Bar; + | ^^^^^ + +error: return value of constructor must be a bare path + --> $DIR/invalid-imports.rs:33:5 + | +33 | fn f() -> &Bar; + | ^^^^^^^^^^^^^^^ + +error: must be Result<...> + --> $DIR/invalid-imports.rs:36:15 + | +36 | fn f() -> u32; + | ^^^ + +error: must be Result<...> + --> $DIR/invalid-imports.rs:38:15 + | +38 | fn f() -> &u32; + | ^^^^ + +error: must have at least one generic parameter + --> $DIR/invalid-imports.rs:40:15 + | +40 | fn f() -> Result<>; + | ^^^^^^^^ + +error: it is currently not sound to use lifetimes in function signatures + --> $DIR/invalid-imports.rs:42:22 + | +42 | fn f() -> Result<'a>; + | ^^ + +error: aborting due to 15 previous errors + diff --git a/crates/macro/ui-tests/invalid-items.rs b/crates/macro/ui-tests/invalid-items.rs new file mode 100644 index 00000000..851800d3 --- /dev/null +++ b/crates/macro/ui-tests/invalid-items.rs @@ -0,0 +1,38 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +fn foo() {} + +#[wasm_bindgen] +pub unsafe fn foo1() {} + +#[wasm_bindgen] +pub const fn foo2() {} + +#[wasm_bindgen] +struct Foo(T); + +#[wasm_bindgen] +extern "C" { + static mut FOO: u32; + + pub fn foo3(x: i32, ...); +} + +#[wasm_bindgen] +extern "system" { +} + +#[wasm_bindgen] +pub fn foo4() {} +#[wasm_bindgen] +pub fn foo5<'a>() {} +#[wasm_bindgen] +pub fn foo6<'a, T>() {} + +#[wasm_bindgen] +trait X {} diff --git a/crates/macro/ui-tests/invalid-items.stderr b/crates/macro/ui-tests/invalid-items.stderr new file mode 100644 index 00000000..6040cf4d --- /dev/null +++ b/crates/macro/ui-tests/invalid-items.stderr @@ -0,0 +1,68 @@ +error: can only #[wasm_bindgen] public functions + --> $DIR/invalid-items.rs:8:1 + | +8 | fn foo() {} + | ^^^^^^^^^^^ + +error: can only #[wasm_bindgen] safe functions + --> $DIR/invalid-items.rs:11:5 + | +11 | pub unsafe fn foo1() {} + | ^^^^^^ + +error: can only #[wasm_bindgen] non-const functions + --> $DIR/invalid-items.rs:14:5 + | +14 | pub const fn foo2() {} + | ^^^^^ + +error: structs with #[wasm_bindgen] cannot have lifetime or type parameters currently + --> $DIR/invalid-items.rs:17:11 + | +17 | struct Foo(T); + | ^^^ + +error: cannot import mutable globals yet + --> $DIR/invalid-items.rs:21:12 + | +21 | static mut FOO: u32; + | ^^^ + +error: can't #[wasm_bindgen] variadic functions + --> $DIR/invalid-items.rs:23:25 + | +23 | pub fn foo3(x: i32, ...); + | ^^^ + +error: only foreign mods with the `C` ABI are allowed + --> $DIR/invalid-items.rs:27:8 + | +27 | extern "system" { + | ^^^^^^^^ + +error: can't #[wasm_bindgen] functions with lifetime or type parameters + --> $DIR/invalid-items.rs:31:12 + | +31 | pub fn foo4() {} + | ^^^ + +error: can't #[wasm_bindgen] functions with lifetime or type parameters + --> $DIR/invalid-items.rs:33:12 + | +33 | pub fn foo5<'a>() {} + | ^^^^ + +error: can't #[wasm_bindgen] functions with lifetime or type parameters + --> $DIR/invalid-items.rs:35:12 + | +35 | pub fn foo6<'a, T>() {} + | ^^^^^^^ + +error: #[wasm_bindgen] can only be applied to a function, struct, enum, impl, or extern block + --> $DIR/invalid-items.rs:38:1 + | +38 | trait X {} + | ^^^^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/crates/macro/ui-tests/invalid-methods.rs b/crates/macro/ui-tests/invalid-methods.rs new file mode 100644 index 00000000..9fcf3f4b --- /dev/null +++ b/crates/macro/ui-tests/invalid-methods.rs @@ -0,0 +1,43 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct A; + +#[wasm_bindgen] +default impl A { +} + +#[wasm_bindgen] +unsafe impl A { +} + +#[wasm_bindgen] +impl Clone for A { +} + +#[wasm_bindgen] +impl A { +} + +#[wasm_bindgen] +impl &'static A { +} + +macro_rules! x { () => () } + +#[wasm_bindgen] +impl A { + const X: u32 = 3; + type Y = u32; + x!(); + + // pub default fn foo() {} // TODO: compiler's pretty printer totally broken + + + pub const fn foo() {} + pub unsafe fn foo() {} +} diff --git a/crates/macro/ui-tests/invalid-methods.stderr b/crates/macro/ui-tests/invalid-methods.stderr new file mode 100644 index 00000000..9ac1b887 --- /dev/null +++ b/crates/macro/ui-tests/invalid-methods.stderr @@ -0,0 +1,62 @@ +error: #[wasm_bindgen] default impls are not supported + --> $DIR/invalid-methods.rs:11:1 + | +11 | default impl A { + | ^^^^^^^ + +error: #[wasm_bindgen] unsafe impls are not supported + --> $DIR/invalid-methods.rs:15:1 + | +15 | unsafe impl A { + | ^^^^^^ + +error: #[wasm_bindgen] trait impls are not supported + --> $DIR/invalid-methods.rs:19:6 + | +19 | impl Clone for A { + | ^^^^^ + +error: #[wasm_bindgen] generic impls aren't supported + --> $DIR/invalid-methods.rs:23:5 + | +23 | impl A { + | ^^^ + +error: unsupported self type in #[wasm_bindgen] impl + --> $DIR/invalid-methods.rs:27:6 + | +27 | impl &'static A { + | ^^^^^^^^^^ + +error: const definitions aren't supported with #[wasm_bindgen] + --> $DIR/invalid-methods.rs:34:5 + | +34 | const X: u32 = 3; + | ^^^^^^^^^^^^^^^^^ + +error: type definitions in impls aren't supported with #[wasm_bindgen] + --> $DIR/invalid-methods.rs:35:5 + | +35 | type Y = u32; + | ^^^^^^^^^^^^^ + +error: macros in impls aren't supported + --> $DIR/invalid-methods.rs:36:5 + | +36 | x!(); + | ^^^^^ + +error: can only #[wasm_bindgen] non-const functions + --> $DIR/invalid-methods.rs:41:9 + | +41 | pub const fn foo() {} + | ^^^^^ + +error: can only bindgen safe functions + --> $DIR/invalid-methods.rs:42:9 + | +42 | pub unsafe fn foo() {} + | ^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/crates/macro/ui-tests/non-public-function.stderr b/crates/macro/ui-tests/non-public-function.stderr index fc25ba51..510dc537 100644 --- a/crates/macro/ui-tests/non-public-function.stderr +++ b/crates/macro/ui-tests/non-public-function.stderr @@ -1,10 +1,8 @@ -error: custom attribute panicked - --> $DIR/non-public-function.rs:7:1 +error: can only #[wasm_bindgen] public functions + --> $DIR/non-public-function.rs:8:1 | -7 | #[wasm_bindgen] - | ^^^^^^^^^^^^^^^ - | - = help: message: can only bindgen public functions +8 | fn foo() {} + | ^^^^^^^^^^^ error: aborting due to previous error