From 2c69d25289bb9385b41decaf78eb2a7620489030 Mon Sep 17 00:00:00 2001 From: "Richard Dodd (dodj)" Date: Thu, 26 Jul 2018 18:09:04 +0100 Subject: [PATCH] Add file location information when failing to parse WebIDL files. (#562) --- crates/web-sys/Cargo.toml | 1 + crates/web-sys/build.rs | 33 ++++++++++++++----- crates/webidl/Cargo.toml | 1 + crates/webidl/src/error.rs | 65 ++++++++++++++++++++++++++++++++++++++ crates/webidl/src/lib.rs | 42 +++++++++++++++++------- 5 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 crates/webidl/src/error.rs diff --git a/crates/web-sys/Cargo.toml b/crates/web-sys/Cargo.toml index f3098cd4..4ed497d9 100644 --- a/crates/web-sys/Cargo.toml +++ b/crates/web-sys/Cargo.toml @@ -8,6 +8,7 @@ readme = "./README.md" env_logger = "0.5.10" failure = "0.1" wasm-bindgen-webidl = { path = "../webidl", version = "=0.2.14" } +sourcefile = "0.1" [dependencies] wasm-bindgen = { path = "../..", version = "=0.2.14" } diff --git a/crates/web-sys/build.rs b/crates/web-sys/build.rs index adb29176..69ec365e 100644 --- a/crates/web-sys/build.rs +++ b/crates/web-sys/build.rs @@ -1,8 +1,10 @@ extern crate env_logger; extern crate failure; extern crate wasm_bindgen_webidl; +extern crate sourcefile; -use failure::ResultExt; +use failure::{Fail, ResultExt}; +use sourcefile::SourceFile; use std::env; use std::ffi::OsStr; use std::fs; @@ -27,20 +29,34 @@ fn try_main() -> Result<(), failure::Error> { println!("cargo:rerun-if-changed=webidls/enabled"); let entries = fs::read_dir("webidls/enabled").context("reading webidls/enabled directory")?; - let mut contents = String::new(); + let mut source = SourceFile::default(); for entry in entries { let entry = entry.context("getting webidls/enabled/*.webidl entry")?; if entry.path().extension() == Some(OsStr::new("webidl")) { println!("cargo:rerun-if-changed={}", entry.path().display()); - - let this_contents = - fs::read_to_string(entry.path()).context("reading WebIDL file contents")?; - contents.push_str(&this_contents); + source = source.add_file(entry.path()) + .with_context(|_| format!("reading contents of file \"{}\"", + entry.path().display()))?; } } - let bindings = wasm_bindgen_webidl::compile(&contents) - .context("compiling WebIDL into wasm-bindgen bindings")?; + let bindings = match wasm_bindgen_webidl::compile(&source.contents) { + Ok(bindings) => bindings, + Err(e) => match e.kind() { + wasm_bindgen_webidl::ErrorKind::ParsingWebIDLSourcePos(pos) => { + if let Some(pos) = source.resolve_offset(pos) { + let ctx = format!("compiling WebIDL into wasm-bindgen bindings in file \ + \"{}\", line {} column {}", pos.filename, pos.line + 1, pos.col + 1); + return Err(e.context(ctx).into()); + } else { + return Err(e.context("compiling WebIDL into wasm-bindgen bindings").into()); + } + } + _ => { + return Err(e.context("compiling WebIDL into wasm-bindgen bindings").into()); + } + } + }; let out_dir = env::var("OUT_DIR").context("reading OUT_DIR environment variable")?; let mut out_file = fs::File::create(path::Path::new(&out_dir).join("bindings.rs")) @@ -51,3 +67,4 @@ fn try_main() -> Result<(), failure::Error> { Ok(()) } + diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index 1d09039b..46e8445f 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -16,6 +16,7 @@ wasm-bindgen-test-project-builder = { path = "../test-project-builder", version [dependencies] failure = "0.1" +failure_derive = "0.1" heck = "0.3" log = "0.4.1" proc-macro2 = "0.4.8" diff --git a/crates/webidl/src/error.rs b/crates/webidl/src/error.rs new file mode 100644 index 00000000..cdf2519d --- /dev/null +++ b/crates/webidl/src/error.rs @@ -0,0 +1,65 @@ +use failure::{Backtrace, Context, Fail}; +use std::fmt; + +/// Either `Ok(t)` or `Err(Error)`. +pub type Result = ::std::result::Result; + +/// The different contexts an error can occur in in this crate. +#[derive(Debug, Fail, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum ErrorKind { + /// Failed to open a WebIDL file. + #[fail(display="opening WebIDL file")] + OpeningWebIDLFile, + /// Failed to read a WebIDL file. + #[fail(display="reading WebIDL file")] + ReadingWebIDLFile, + /// Failed to parse a WebIDL file. + #[fail(display="parsing WebIDL source text at {}", _0)] + ParsingWebIDLSourcePos(usize), + /// Failed to parse a WebIDL file. + #[fail(display="parsing WebIDL source text")] + ParsingWebIDLSource, +} + +/// The error type for this crate. +#[derive(Debug)] +pub struct Error { + inner: Context, +} + +impl Fail for Error { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl Error { + /// The context for this error. + pub fn kind(&self) -> ErrorKind { + *self.inner.get_context() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Error { inner: Context::new(kind) } + } +} + +impl From> for Error { + fn from(inner: Context) -> Error { + Error { inner: inner } + } +} + + diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 129fbe43..6ec89873 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -10,6 +10,8 @@ emitted for the types and methods described in the WebIDL. #![doc(html_root_url = "https://docs.rs/wasm-bindgen-webidl/0.2")] extern crate failure; +#[macro_use] +extern crate failure_derive; extern crate heck; #[macro_use] extern crate log; @@ -23,38 +25,54 @@ extern crate webidl; mod first_pass; mod util; +mod error; use std::collections::BTreeSet; use std::fs; use std::io::{self, Read}; -use std::iter::{self, FromIterator}; +use std::iter::FromIterator; use std::path::Path; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; -use backend::util::{ident_ty, raw_ident, rust_ident, wrap_import_function}; -use failure::ResultExt; +use backend::util::{ident_ty, rust_ident, wrap_import_function}; +use failure::{ResultExt, Fail}; use heck::{CamelCase, 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}; -/// Either `Ok(t)` or `Err(failure::Error)`. -pub type Result = ::std::result::Result; +pub use error::{Error, ErrorKind, Result}; /// Parse the WebIDL at the given path into a wasm-bindgen AST. fn parse_file(webidl_path: &Path) -> Result { - let file = fs::File::open(webidl_path).context("opening WebIDL file")?; + let file = fs::File::open(webidl_path).context(ErrorKind::OpeningWebIDLFile)?; let mut file = io::BufReader::new(file); let mut source = String::new(); - file.read_to_string(&mut source) - .context("reading WebIDL file")?; + file.read_to_string(&mut source).context(ErrorKind::ReadingWebIDLFile)?; parse(&source) } /// Parse a string of WebIDL source text into a wasm-bindgen AST. fn parse(webidl_source: &str) -> Result { - let definitions = webidl::parse_string(webidl_source).context("parsing WebIDL source text")?; + let definitions = match webidl::parse_string(webidl_source) { + Ok(def) => def, + Err(e) => { + let kind = match &e { + webidl::ParseError::InvalidToken { location } => { + ErrorKind::ParsingWebIDLSourcePos(*location) + } + webidl::ParseError::UnrecognizedToken { token: Some((start, ..)), .. } => { + ErrorKind::ParsingWebIDLSourcePos(*start) + } + webidl::ParseError::ExtraToken { token: (start, ..) } => { + ErrorKind::ParsingWebIDLSourcePos(*start) + }, + _ => ErrorKind::ParsingWebIDLSource + }; + return Err(e.context(kind).into()); + } + }; let mut first_pass_record = Default::default(); definitions.first_pass(&mut first_pass_record)?; @@ -528,9 +546,9 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { impl<'a> WebidlParse<&'a str> for webidl::ast::Iterable { fn webidl_parse( &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + _program: &mut backend::ast::Program, + _first_pass: &FirstPassRecord<'_>, + _self_name: &'a str, ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(());