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() }