From c4dcaee1b93b4dff748412f2d31f997e9d7d9273 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 1 Aug 2018 17:15:27 -0500 Subject: [PATCH] Prepare to have targeted error diagnostics (#604) This commit starts to add infrastructure for targeted diagnostics in the `#[wasm_bindgen]` attribute, intended eventually at providing much better errors as they'll be pointing to exactly the code in question rather than always to a `#[wasm_bindgen]` attribute. The general changes are are: * A new `Diagnostic` error type is added to the backend. A `Diagnostic` is created with a textual error or with a span, and it can also be created from a list of diagnostics. A `Diagnostic` implements `ToTokens` which emits a bunch of invocations of `compile_error!` that will cause rustc to later generate errors. * Fallible implementations of `ToTokens` have switched to using a new trait, `TryToTokens`, which returns a `Result` to use `?` with. * The `MacroParse` trait has changed to returning a `Result` to propagate errors upwards. * A new `ui-tests` crate was added which uses `compiletest_rs` to add UI tests. These UI tests will verify that our output improves over time and does not regress. This test suite is added to CI as a new builder as well. * No `Diagnostic` instances are created just yet, everything continues to panic and return `Ok`, with the one exception of the top-level invocations of `syn::parse` which now create a `Diagnostic` and pass it along. This commit does not immediately improve diagnostics but the intention is that it is laying the groundwork for improving diagnostics over time. It should ideally be much easier to contribute improved diagnostics after this commit! cc #601 --- .travis.yml | 6 ++ Cargo.toml | 1 + crates/backend/src/ast.rs | 18 ++-- crates/backend/src/codegen.rs | 50 +++++++---- crates/backend/src/error.rs | 83 +++++++++++++++++++ crates/backend/src/lib.rs | 4 + crates/macro-support/src/lib.rs | 21 +++-- crates/macro-support/src/parser.rs | 41 ++++++--- crates/macro/Cargo.toml | 5 +- crates/macro/README.md | 28 +++++++ crates/macro/src/lib.rs | 22 +++-- crates/macro/ui-tests/Cargo.toml | 15 ++++ .../ui-tests/attribute-fails-to-parse.rs | 8 ++ .../ui-tests/attribute-fails-to-parse.stderr | 8 ++ crates/macro/ui-tests/non-public-function.rs | 8 ++ .../macro/ui-tests/non-public-function.stderr | 10 +++ crates/macro/ui-tests/test.rs | 25 ++++++ .../macro/ui-tests/update-all-references.sh | 23 +++++ crates/macro/ui-tests/update-references.sh | 42 ++++++++++ crates/webidl/src/lib.rs | 6 +- 20 files changed, 370 insertions(+), 54 deletions(-) create mode 100644 crates/backend/src/error.rs create mode 100644 crates/macro/README.md create mode 100644 crates/macro/ui-tests/Cargo.toml create mode 100644 crates/macro/ui-tests/attribute-fails-to-parse.rs create mode 100644 crates/macro/ui-tests/attribute-fails-to-parse.stderr create mode 100644 crates/macro/ui-tests/non-public-function.rs create mode 100644 crates/macro/ui-tests/non-public-function.stderr create mode 100644 crates/macro/ui-tests/test.rs create mode 100755 crates/macro/ui-tests/update-all-references.sh create mode 100755 crates/macro/ui-tests/update-references.sh diff --git a/.travis.yml b/.travis.yml index 22a7f531..91eced91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -117,6 +117,12 @@ matrix: - cargo test -p webidl-tests --target wasm32-unknown-unknown if: branch = master + # UI tests for the macro work just fine + - rust: nightly + env: JOB=macro-ui + script: cargo test -p ui-tests + if: branch = master + # Dist linux binary - rust: nightly env: JOB=dist-linux TARGET=x86_64-unknown-linux-musl diff --git a/Cargo.toml b/Cargo.toml index 26b519e6..a3c8b84f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ members = [ "crates/test", "crates/test/sample", "crates/typescript", + "crates/macro/ui-tests", "crates/web-sys", "crates/webidl", "crates/webidl-tests", diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 4b491fd0..823437ab 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -2,6 +2,8 @@ use proc_macro2::{Ident, Span}; use shared; use syn; +use Diagnostic; + /// An abstract syntax tree representing a rust program. Contains /// extra information for joining up this rust code with javascript. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] @@ -223,15 +225,17 @@ pub enum ConstValue { } impl Program { - pub(crate) fn shared(&self) -> shared::Program { - shared::Program { + pub(crate) fn shared(&self) -> Result { + Ok(shared::Program { exports: self.exports.iter().map(|a| a.shared()).collect(), structs: self.structs.iter().map(|a| a.shared()).collect(), enums: self.enums.iter().map(|a| a.shared()).collect(), - imports: self.imports.iter().map(|a| a.shared()).collect(), + imports: self.imports.iter() + .map(|a| a.shared()) + .collect::>()?, version: shared::version(), schema_version: shared::SCHEMA_VERSION.to_string(), - } + }) } } @@ -305,7 +309,7 @@ impl Variant { } impl Import { - fn shared(&self) -> shared::Import { + fn shared(&self) -> Result { match (&self.module, &self.version) { (&Some(ref m), None) if m.starts_with("./") => {} (&Some(ref m), &Some(_)) if m.starts_with("./") => { @@ -330,12 +334,12 @@ impl Import { } (&None, &None) => {} } - shared::Import { + Ok(shared::Import { module: self.module.clone(), version: self.version.clone(), js_namespace: self.js_namespace.as_ref().map(|s| s.to_string()), kind: self.kind.shared(), - } + }) } } diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index f14bbd94..cd0d9279 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -2,19 +2,31 @@ use std::collections::HashSet; use std::sync::Mutex; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; -use ast; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::ToTokens; use serde_json; use shared; use syn; + +use ast; +use Diagnostic; use util::ShortHash; -impl ToTokens for ast::Program { +pub trait TryToTokens { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic>; + + fn try_to_token_stream(&self) -> Result { + let mut tokens = TokenStream::new(); + self.try_to_tokens(&mut tokens)?; + Ok(tokens) + } +} + +impl TryToTokens for ast::Program { // Generate wrappers for all the items that we've found - fn to_tokens(&self, tokens: &mut TokenStream) { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { for export in self.exports.iter() { - export.to_tokens(tokens); + export.try_to_tokens(tokens)?; } for s in self.structs.iter() { s.to_tokens(tokens); @@ -30,13 +42,13 @@ impl ToTokens for ast::Program { if let Some(ns) = &i.js_namespace { if types.contains(ns) && i.kind.fits_on_impl() { - let kind = &i.kind; + let kind = i.kind.try_to_token_stream()?; (quote! { impl #ns { #kind } }).to_tokens(tokens); continue; } } - i.kind.to_tokens(tokens); + i.kind.try_to_tokens(tokens)?; } for e in self.enums.iter() { e.to_tokens(tokens); @@ -60,7 +72,7 @@ impl ToTokens for ast::Program { ); let generated_static_name = Ident::new(&generated_static_name, Span::call_site()); - let description = serde_json::to_string(&self.shared()).unwrap(); + let description = serde_json::to_string(&self.shared()?).unwrap(); // Each JSON blob is prepended with the length of the JSON blob so when // all these sections are concatenated in the final wasm file we know @@ -83,6 +95,8 @@ impl ToTokens for ast::Program { pub static #generated_static_name: [u8; #generated_static_length] = *#generated_static_value; }).to_tokens(tokens); + + Ok(()) } } @@ -276,8 +290,10 @@ impl ToTokens for ast::StructField { } } -impl ToTokens for ast::Export { - fn to_tokens(self: &ast::Export, into: &mut TokenStream) { +impl TryToTokens for ast::Export { + fn try_to_tokens(self: &ast::Export, into: &mut TokenStream) + -> Result<(), Diagnostic> + { let generated_name = self.rust_symbol(); let export_name = self.export_name(); let mut args = vec![]; @@ -461,17 +477,21 @@ impl ToTokens for ast::Export { #(<#argtys as WasmDescribe>::describe();)* #describe_ret }).to_tokens(into); + + Ok(()) } } -impl ToTokens for ast::ImportKind { - fn to_tokens(&self, tokens: &mut TokenStream) { +impl TryToTokens for ast::ImportKind { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { match *self { - ast::ImportKind::Function(ref f) => f.to_tokens(tokens), + ast::ImportKind::Function(ref f) => f.try_to_tokens(tokens)?, ast::ImportKind::Static(ref s) => s.to_tokens(tokens), ast::ImportKind::Type(ref t) => t.to_tokens(tokens), ast::ImportKind::Enum(ref e) => e.to_tokens(tokens), } + + Ok(()) } } @@ -663,8 +683,8 @@ impl ToTokens for ast::ImportEnum { } } -impl ToTokens for ast::ImportFunction { - fn to_tokens(&self, tokens: &mut TokenStream) { +impl TryToTokens for ast::ImportFunction { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut class_ty = None; let mut is_method = false; match self.kind { @@ -827,6 +847,8 @@ impl ToTokens for ast::ImportFunction { } else { invocation.to_tokens(tokens); } + + Ok(()) } } diff --git a/crates/backend/src/error.rs b/crates/backend/src/error.rs new file mode 100644 index 00000000..b286c922 --- /dev/null +++ b/crates/backend/src/error.rs @@ -0,0 +1,83 @@ +use proc_macro2::*; +use quote::ToTokens; + +pub struct Diagnostic { + inner: Repr, +} +enum Repr { + Single { + text: String, + span: Option<(Span, Span)>, + }, + Multi { + diagnostics: Vec, + } +} + +impl Diagnostic { + pub fn error>(text: T) -> Diagnostic { + Diagnostic { + inner: Repr::Single { + text: text.into(), + span: None, + } + } + } + + pub fn span_error>(node: &ToTokens, text: T) -> Diagnostic { + Diagnostic { + inner: Repr::Single { + text: text.into(), + span: extract_spans(node), + } + } + } + + pub fn from_vec(diagnostics: Vec) -> Result<(), Diagnostic> { + if diagnostics.len() == 0 { + Ok(()) + } else { + Err(Diagnostic { inner: Repr::Multi { diagnostics } }) + } + } + + #[allow(unconditional_recursion)] + pub fn panic(&self) -> ! { + match &self.inner { + Repr::Single { text, .. } => panic!("{}", text), + Repr::Multi { diagnostics } => diagnostics[0].panic(), + } + } +} + +fn extract_spans(node: &ToTokens) -> Option<(Span, Span)> { + let mut t = TokenStream::new(); + node.to_tokens(&mut t); + let mut tokens = t.into_iter(); + let start = tokens.next().map(|t| t.span()); + let end = tokens.last().map(|t| t.span()); + start.map(|start| (start, end.unwrap_or(start))) +} + +impl ToTokens for Diagnostic { + fn to_tokens(&self, dst: &mut TokenStream) { + match &self.inner { + Repr::Single { text, span } => { + let cs2 = (Span::call_site(), Span::call_site()); + let (start, end) = span.unwrap_or(cs2); + dst.extend(Some(Ident::new("compile_error", start).into())); + dst.extend(Some(Punct::new('!', Spacing::Alone).into())); + let mut message = TokenStream::new(); + message.extend(Some(Literal::string(text).into())); + let mut group = Group::new(Delimiter::Brace, message); + group.set_span(end); + dst.extend(Some(group.into())); + } + Repr::Multi { diagnostics } => { + for diagnostic in diagnostics { + diagnostic.to_tokens(dst); + } + } + } + } +} diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs index 4edb13d3..1147d76f 100755 --- a/crates/backend/src/lib.rs +++ b/crates/backend/src/lib.rs @@ -14,7 +14,11 @@ extern crate syn; extern crate wasm_bindgen_shared as shared; +pub use codegen::TryToTokens; +pub use error::Diagnostic; + pub mod ast; mod codegen; pub mod defined; +mod error; pub mod util; diff --git a/crates/macro-support/src/lib.rs b/crates/macro-support/src/lib.rs index a38be868..9ec85916 100755 --- a/crates/macro-support/src/lib.rs +++ b/crates/macro-support/src/lib.rs @@ -12,15 +12,26 @@ extern crate wasm_bindgen_shared as shared; pub use parser::BindgenAttrs; use parser::MacroParse; -use quote::ToTokens; +use backend::{Diagnostic, TryToTokens}; +use proc_macro2::TokenStream; mod parser; /// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings -pub fn expand(item: syn::Item, opts: parser::BindgenAttrs) -> proc_macro2::TokenStream { +pub fn expand(attr: TokenStream, input: TokenStream) -> Result { + let item = syn_parse::(input, "rust item")?; + let opts = syn_parse(attr, "#[wasm_bindgen] attribute options")?; + let mut tokens = proc_macro2::TokenStream::new(); let mut program = backend::ast::Program::default(); - item.macro_parse(&mut program, (Some(opts), &mut tokens)); - program.to_tokens(&mut tokens); - tokens + item.macro_parse(&mut program, (Some(opts), &mut tokens))?; + program.try_to_tokens(&mut tokens)?; + Ok(tokens) +} + +fn syn_parse(tokens: TokenStream, name: &str) -> Result { + syn::parse2(tokens.clone()) + .map_err(|err| { + Diagnostic::span_error(&tokens, format!("error parsing {}: {}", name, err)) + }) } diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 703e09ea..7f353dc9 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1,4 +1,5 @@ use backend::ast; +use backend::Diagnostic; use backend::util::{ident_ty, ShortHash}; use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::ToTokens; @@ -590,7 +591,8 @@ pub(crate) trait MacroParse { /// /// The context is used to have access to the attributes on `#[wasm_bindgen]`, and to allow /// writing to the output `TokenStream`. - fn macro_parse(self, program: &mut ast::Program, context: Ctx); + fn macro_parse(self, program: &mut ast::Program, context: Ctx) + -> Result<(), Diagnostic>; } impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { @@ -598,7 +600,7 @@ impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { self, program: &mut ast::Program, (opts, tokens): (Option, &'a mut TokenStream), - ) { + ) -> Result<(), Diagnostic> { match self { syn::Item::Fn(mut f) => { let no_mangle = f @@ -629,27 +631,31 @@ impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { s.to_tokens(tokens); } syn::Item::Impl(mut i) => { - (&mut i).macro_parse(program, ()); + (&mut i).macro_parse(program, ())?; i.to_tokens(tokens); } syn::Item::ForeignMod(mut f) => { let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs)); - f.macro_parse(program, opts); + f.macro_parse(program, opts)?; } syn::Item::Enum(e) => { e.to_tokens(tokens); - e.macro_parse(program, ()); + e.macro_parse(program, ())?; } _ => panic!( "#[wasm_bindgen] can only be applied to a function, \ struct, enum, impl, or extern block" ), } + + Ok(()) } } impl<'a> MacroParse<()> for &'a mut syn::ItemImpl { - fn macro_parse(self, program: &mut ast::Program, (): ()) { + fn macro_parse(self, program: &mut ast::Program, (): ()) + -> Result<(), Diagnostic> + { if self.defaultness.is_some() { panic!("default impls are not supported"); } @@ -672,14 +678,20 @@ impl<'a> MacroParse<()> for &'a mut syn::ItemImpl { }, _ => panic!("unsupported self type in impl"), }; + let mut errors = Vec::new(); for item in self.items.iter_mut() { - (&name, item).macro_parse(program, ()) + if let Err(e) = (&name, item).macro_parse(program, ()) { + errors.push(e); + } } + Diagnostic::from_vec(errors) } } impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { - fn macro_parse(self, program: &mut ast::Program, (): ()) { + fn macro_parse(self, program: &mut ast::Program, (): ()) + -> Result<(), Diagnostic> + { let (class, item) = self; replace_self(class, item); let method = match item { @@ -691,7 +703,7 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { }; match method.vis { syn::Visibility::Public(_) => {} - _ => return, + _ => return Ok(()), } if method.defaultness.is_some() { panic!("default methods are not supported"); @@ -728,11 +740,14 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { comments, rust_name: method.sig.ident.clone(), }); + Ok(()) } } impl MacroParse<()> for syn::ItemEnum { - fn macro_parse(self, program: &mut ast::Program, (): ()) { + fn macro_parse(self, program: &mut ast::Program, (): ()) + -> Result<(), Diagnostic> + { match self.vis { syn::Visibility::Public(_) => {} _ => panic!("only public enums are allowed"), @@ -776,11 +791,14 @@ impl MacroParse<()> for syn::ItemEnum { variants, comments, }); + Ok(()) } } impl MacroParse for syn::ItemForeignMod { - fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) { + fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) + -> Result<(), Diagnostic> + { match self.abi.name { Some(ref l) if l.value() == "C" => {} None => {} @@ -816,6 +834,7 @@ impl MacroParse for syn::ItemForeignMod { kind, }); } + Ok(()) } } diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index 2b2b327f..9851e91e 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -14,10 +14,9 @@ Definition of the `#[wasm_bindgen]` attribute, an internal dependency proc-macro = true [features] -spans = ["proc-macro2/nightly", "wasm-bindgen-macro-support/spans"] +spans = ["wasm-bindgen-macro-support/spans"] xxx_debug_only_print_generated_code = [] [dependencies] -syn = { version = '0.14', features = ['full'] } -proc-macro2 = "0.4.9" wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.15" } +quote = "0.6" diff --git a/crates/macro/README.md b/crates/macro/README.md new file mode 100644 index 00000000..35382986 --- /dev/null +++ b/crates/macro/README.md @@ -0,0 +1,28 @@ +# `wasm-bindgen-macro` + +Implementation of the `#[wasm_bindgen]` attribute. See the `wasm-bindgen` +documentation for more information about what this macro does. + +## Testing + +Testing of this macro is done through "ui tests" in the `ui-tests` subdirectory +of this crate. Each Rust file in this folder is compiled with the `wasm_bindgen` +crate, and the `*.stderr` file sitting next to it is the asserted output of the +compiler. If the output matches, the test passes, and if the output doesn't +match the test fails. Note that it is also considered a failure if a test +actually compiles successfully. + +To add a test: + +* Create `ui-tests/my-awesome-test.rs` +* Write an invalid `#[wasm_bindgen]` invocation, testing the error you're + generating +* Execute `cargo test -p ui-tests`, the test will fail +* From within the `ui-tests` folder, execute `./update-all-references.sh`. This + should create a `my-awesome-test.stderr` file. +* Inspect `my-awesome-test.stderr` to make sure it looks ok +* Rerun `cargo test -p ui-tests` and your tests should pass! + +Testing here is a work in progress, see +[#601](https://github.com/rustwasm/wasm-bindgen/issues/601) for more +information. diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index b0eceb63..f94d2b52 100755 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -1,23 +1,21 @@ #![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro/0.2")] extern crate proc_macro; -extern crate proc_macro2; -extern crate syn; +#[macro_use] +extern crate quote; extern crate wasm_bindgen_macro_support as macro_support; -use macro_support::BindgenAttrs; use proc_macro::TokenStream; #[proc_macro_attribute] pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { - let item = syn::parse::(input.clone()).expect("expected a valid Rust item"); - let opts = syn::parse::(attr).expect("invalid arguments to #[wasm_bindgen]"); - - let tokens = macro_support::expand(item, opts); - - if cfg!(feature = "xxx_debug_only_print_generated_code") { - println!("{}", tokens); + match macro_support::expand(attr.into(), input.into()) { + Ok(tokens) => { + if cfg!(feature = "xxx_debug_only_print_generated_code") { + println!("{}", tokens); + } + tokens.into() + } + Err(diagnostic) => (quote! { #diagnostic }).into(), } - - tokens.into() } diff --git a/crates/macro/ui-tests/Cargo.toml b/crates/macro/ui-tests/Cargo.toml new file mode 100644 index 00000000..ce105e8c --- /dev/null +++ b/crates/macro/ui-tests/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ui-tests" +version = "0.1.0" +authors = ["The wasm-bindgen Authors"] + +[lib] +path = "test.rs" +doctest = false +harness = false + +[dependencies] +wasm-bindgen = { path = "../../.." } + +[dev-dependencies] +compiletest_rs = "0.3" diff --git a/crates/macro/ui-tests/attribute-fails-to-parse.rs b/crates/macro/ui-tests/attribute-fails-to-parse.rs new file mode 100644 index 00000000..514876fa --- /dev/null +++ b/crates/macro/ui-tests/attribute-fails-to-parse.rs @@ -0,0 +1,8 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(nonsense)] +pub fn foo() {} diff --git a/crates/macro/ui-tests/attribute-fails-to-parse.stderr b/crates/macro/ui-tests/attribute-fails-to-parse.stderr new file mode 100644 index 00000000..dbe5f5d5 --- /dev/null +++ b/crates/macro/ui-tests/attribute-fails-to-parse.stderr @@ -0,0 +1,8 @@ +error: error parsing #[wasm_bindgen] attribute options: failed to parse anything + --> $DIR/attribute-fails-to-parse.rs:7:16 + | +7 | #[wasm_bindgen(nonsense)] + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/crates/macro/ui-tests/non-public-function.rs b/crates/macro/ui-tests/non-public-function.rs new file mode 100644 index 00000000..8f0b117d --- /dev/null +++ b/crates/macro/ui-tests/non-public-function.rs @@ -0,0 +1,8 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +fn foo() {} diff --git a/crates/macro/ui-tests/non-public-function.stderr b/crates/macro/ui-tests/non-public-function.stderr new file mode 100644 index 00000000..fc25ba51 --- /dev/null +++ b/crates/macro/ui-tests/non-public-function.stderr @@ -0,0 +1,10 @@ +error: custom attribute panicked + --> $DIR/non-public-function.rs:7:1 + | +7 | #[wasm_bindgen] + | ^^^^^^^^^^^^^^^ + | + = help: message: can only bindgen public functions + +error: aborting due to previous error + diff --git a/crates/macro/ui-tests/test.rs b/crates/macro/ui-tests/test.rs new file mode 100644 index 00000000..3abb40af --- /dev/null +++ b/crates/macro/ui-tests/test.rs @@ -0,0 +1,25 @@ +// ignore-test - not a test + +#![cfg(test)] + +extern crate compiletest_rs as compiletest; + +use std::env; +use std::fs; +use std::path::PathBuf; + +fn main() { + let mut config = compiletest::Config::default(); + config.mode = "ui".parse().expect("invalid mode"); + let mut me = env::current_exe().unwrap(); + me.pop(); + config.target_rustcflags = Some(format!("-L {}", me.display())); + let src = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + config.src_base = src; + + me.pop(); + me.pop(); + config.build_base = me.join("tests/ui"); + drop(fs::remove_dir_all(&config.build_base)); + compiletest::run_tests(&config); +} diff --git a/crates/macro/ui-tests/update-all-references.sh b/crates/macro/ui-tests/update-all-references.sh new file mode 100755 index 00000000..7b757a47 --- /dev/null +++ b/crates/macro/ui-tests/update-all-references.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +# A script to update the references for all tests. The idea is that +# you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. You then +# run this script, which will copy those files over. If you find +# yourself manually editing a foo.stderr file, you're doing it wrong. +# +# See all `update-references.sh`, if you just want to update a single test. + +MY_DIR=$(dirname $0) +cd $MY_DIR +find . -name '*.rs' | xargs ./update-references.sh diff --git a/crates/macro/ui-tests/update-references.sh b/crates/macro/ui-tests/update-references.sh new file mode 100755 index 00000000..19a6d488 --- /dev/null +++ b/crates/macro/ui-tests/update-references.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +# A script to update the references for particular tests. The idea is +# that you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. This +# script will then copy that output and replace the "expected output" +# files. You can then commit the changes. +# +# If you find yourself manually editing a foo.stderr file, you're +# doing it wrong. + +MYDIR=$(dirname $0) + +BUILD_DIR="../../../target/tests/ui" + +while [[ "$1" != "" ]]; do + STDERR_NAME="${1/%.rs/.stderr}" + STDOUT_NAME="${1/%.rs/.stdout}" + shift + if [ -f $BUILD_DIR/$STDOUT_NAME ] && \ + ! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then + echo updating $MYDIR/$STDOUT_NAME + cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME + fi + if [ -f $BUILD_DIR/$STDERR_NAME ] && \ + ! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then + echo updating $MYDIR/$STDERR_NAME + cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME + fi +done + + diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 19778c9e..399c233c 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -33,11 +33,11 @@ use std::io::{self, Read}; use std::iter::FromIterator; use std::path::Path; +use backend::TryToTokens; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; use backend::util::{ident_ty, rust_ident, wrap_import_function}; use failure::{ResultExt, Fail}; use heck::{ShoutySnakeCase}; -use quote::ToTokens; use first_pass::{FirstPass, FirstPassRecord}; use util::{public, webidl_const_ty_to_syn_ty, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; @@ -111,7 +111,9 @@ fn compile_ast(mut ast: backend::ast::Program) -> String { ast.remove_undefined_imports(&|id| defined.contains(id)); let mut tokens = proc_macro2::TokenStream::new(); - ast.to_tokens(&mut tokens); + if let Err(e) = ast.try_to_tokens(&mut tokens) { + e.panic(); + } tokens.to_string() }