diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/failure_derive/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/failure_derive/Cargo.toml | 27 | ||||
-rw-r--r-- | third_party/rust/failure_derive/build.rs | 39 | ||||
-rw-r--r-- | third_party/rust/failure_derive/src/lib.rs | 266 | ||||
-rw-r--r-- | third_party/rust/failure_derive/tests/backtrace.rs | 64 | ||||
-rw-r--r-- | third_party/rust/failure_derive/tests/custom_type_bounds.rs | 45 | ||||
-rw-r--r-- | third_party/rust/failure_derive/tests/no_derive_display.rs | 21 | ||||
-rw-r--r-- | third_party/rust/failure_derive/tests/tests.rs | 55 | ||||
-rw-r--r-- | third_party/rust/failure_derive/tests/wraps.rs | 98 |
9 files changed, 616 insertions, 0 deletions
diff --git a/third_party/rust/failure_derive/.cargo-checksum.json b/third_party/rust/failure_derive/.cargo-checksum.json new file mode 100644 index 0000000000..3a26be720c --- /dev/null +++ b/third_party/rust/failure_derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"40252e61dfb5eaaa297f868379972f5135e1e8e2d37a8b31858c00d3e765282e","build.rs":"c7481c027fc4c09e2d6dc8ac642ec5314b6a02269fb022b0f3c1850f000d31f5","src/lib.rs":"c446d83b42f5aca4453da4e2dd8e40dbfe9aa33ba1a038217174ece5158b7196","tests/backtrace.rs":"56b2b97f83e5341108aac403a50491560dc6d897d6002973248c9c9014c0e494","tests/custom_type_bounds.rs":"745d3e488738f5d90bcb1fa66a6784b64a88881b1d62c241cba2f863f4eb31c8","tests/no_derive_display.rs":"3ab159aadf809a95d578b4909470f6ecca24cea0caf514509d326601cc7b933b","tests/tests.rs":"eaf131c01cc101b94fd6478a9964f5e3d919f2260f6d327f0e435ac3520fe71e","tests/wraps.rs":"cc2bc5a5555756082ec570afa310939ba7f74a20898991a89fab75232380fd21"},"package":null}
\ No newline at end of file diff --git a/third_party/rust/failure_derive/Cargo.toml b/third_party/rust/failure_derive/Cargo.toml new file mode 100644 index 0000000000..ec45dc0b1d --- /dev/null +++ b/third_party/rust/failure_derive/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["Without Boats <woboats@gmail.com>"] +description = "derives for the failure crate" +license = "MIT OR Apache-2.0" +name = "failure_derive" +repository = "https://github.com/withoutboats/failure_derive" +homepage = "https://rust-lang-nursery.github.io/failure/" +documentation = "https://docs.rs/failure" +version = "0.1.6" +build = "build.rs" + +[dependencies] +quote = "1" +syn = "1.0.3" +synstructure = "0.12.0" +proc-macro2 = "1" + +[dev-dependencies.failure] +version = "0.1.0" +path = ".." + +[lib] +proc-macro = true + +[features] +# This is kept for backward-compatibility reasons, but is otherwise a no-op +std = [] diff --git a/third_party/rust/failure_derive/build.rs b/third_party/rust/failure_derive/build.rs new file mode 100644 index 0000000000..8f220458d2 --- /dev/null +++ b/third_party/rust/failure_derive/build.rs @@ -0,0 +1,39 @@ +use std::env; +use std::process::Command; +use std::str; +use std::str::FromStr; + +fn main() { + if rustc_has_dyn_trait() { + println!("cargo:rustc-cfg=has_dyn_trait"); + } +} + +fn rustc_has_dyn_trait() -> bool { + let rustc = match env::var_os("RUSTC") { + Some(rustc) => rustc, + None => return false, + }; + + let output = match Command::new(rustc).arg("--version").output() { + Ok(output) => output, + Err(_) => return false, + }; + + let version = match str::from_utf8(&output.stdout) { + Ok(version) => version, + Err(_) => return false, + }; + + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return true; + } + + let next = match pieces.next() { + Some(next) => next, + None => return false, + }; + + u32::from_str(next).unwrap_or(0) >= 27 +} diff --git a/third_party/rust/failure_derive/src/lib.rs b/third_party/rust/failure_derive/src/lib.rs new file mode 100644 index 0000000000..fe9e76c4e3 --- /dev/null +++ b/third_party/rust/failure_derive/src/lib.rs @@ -0,0 +1,266 @@ +extern crate proc_macro2; +extern crate syn; + +#[macro_use] +extern crate synstructure; +#[macro_use] +extern crate quote; + +use proc_macro2::{TokenStream, Span}; +use syn::LitStr; +use syn::spanned::Spanned; + +#[derive(Debug)] +struct Error(TokenStream); + +impl Error { + fn new(span: Span, message: &str) -> Error { + Error(quote_spanned! { span => + compile_error!(#message); + }) + } + + fn into_tokens(self) -> TokenStream { + self.0 + } +} + +impl From<syn::Error> for Error { + fn from(e: syn::Error) -> Error { + Error(e.to_compile_error()) + } +} + +decl_derive!([Fail, attributes(fail, cause)] => fail_derive); + +fn fail_derive(s: synstructure::Structure) -> TokenStream { + match fail_derive_impl(s) { + Err(err) => err.into_tokens(), + Ok(tokens) => tokens, + } +} + +fn fail_derive_impl(s: synstructure::Structure) -> Result<TokenStream, Error> { + let make_dyn = if cfg!(has_dyn_trait) { + quote! { &dyn } + } else { + quote! { & } + }; + + let ty_name = LitStr::new(&s.ast().ident.to_string(), Span::call_site()); + + let cause_body = s.each_variant(|v| { + if let Some(cause) = v.bindings().iter().find(is_cause) { + quote!(return Some(::failure::AsFail::as_fail(#cause))) + } else { + quote!(return None) + } + }); + + let bt_body = s.each_variant(|v| { + if let Some(bi) = v.bindings().iter().find(is_backtrace) { + quote!(return Some(#bi)) + } else { + quote!(return None) + } + }); + + let fail = s.unbound_impl( + quote!(::failure::Fail), + quote! { + fn name(&self) -> Option<&str> { + Some(concat!(module_path!(), "::", #ty_name)) + } + + #[allow(unreachable_code)] + fn cause(&self) -> ::failure::_core::option::Option<#make_dyn(::failure::Fail)> { + match *self { #cause_body } + None + } + + #[allow(unreachable_code)] + fn backtrace(&self) -> ::failure::_core::option::Option<&::failure::Backtrace> { + match *self { #bt_body } + None + } + }, + ); + let display = display_body(&s)?.map(|display_body| { + s.unbound_impl( + quote!(::failure::_core::fmt::Display), + quote! { + #[allow(unreachable_code)] + fn fmt(&self, f: &mut ::failure::_core::fmt::Formatter) -> ::failure::_core::fmt::Result { + match *self { #display_body } + write!(f, "An error has occurred.") + } + }, + ) + }); + + Ok(quote! { + #fail + #display + }) +} + +fn display_body(s: &synstructure::Structure) -> Result<Option<quote::__rt::TokenStream>, Error> { + let mut msgs = s.variants().iter().map(|v| find_error_msg(&v.ast().attrs)); + if msgs.all(|msg| msg.map(|m| m.is_none()).unwrap_or(true)) { + return Ok(None); + } + + let mut tokens = TokenStream::new(); + for v in s.variants() { + let msg = + find_error_msg(&v.ast().attrs)? + .ok_or_else(|| Error::new( + v.ast().ident.span(), + "All variants must have display attribute." + ))?; + if msg.nested.is_empty() { + return Err(Error::new( + msg.span(), + "Expected at least one argument to fail attribute" + )); + } + + let format_string = match msg.nested[0] { + syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) if nv.path.is_ident("display") => { + nv.lit.clone() + } + _ => { + return Err(Error::new( + msg.span(), + "Fail attribute must begin `display = \"\"` to control the Display message." + )); + } + }; + let args = msg.nested.iter().skip(1).map(|arg| match *arg { + syn::NestedMeta::Lit(syn::Lit::Int(ref i)) => { + let bi = &v.bindings()[i.base10_parse::<usize>()?]; + Ok(quote!(#bi)) + } + syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => { + let id_s = path.get_ident().map(syn::Ident::to_string).unwrap_or("".to_string()); + if id_s.starts_with("_") { + if let Ok(idx) = id_s[1..].parse::<usize>() { + let bi = match v.bindings().get(idx) { + Some(bi) => bi, + None => { + return Err(Error::new( + arg.span(), + &format!( + "display attempted to access field `{}` in `{}::{}` which \ + does not exist (there are {} field{})", + idx, + s.ast().ident, + v.ast().ident, + v.bindings().len(), + if v.bindings().len() != 1 { "s" } else { "" } + ) + )); + } + }; + return Ok(quote!(#bi)); + } + } + for bi in v.bindings() { + let id = bi.ast().ident.as_ref(); + if id.is_some() && path.is_ident(id.unwrap()) { + return Ok(quote!(#bi)); + } + } + return Err(Error::new( + arg.span(), + &format!( + "Couldn't find field `{:?}` in `{}::{}`", + path, + s.ast().ident, + v.ast().ident + ) + )); + } + ref arg => { + return Err(Error::new( + arg.span(), + "Invalid argument to fail attribute!" + )); + }, + }); + let args = args.collect::<Result<Vec<_>, _>>()?; + + let pat = v.pat(); + tokens.extend(quote!(#pat => { return write!(f, #format_string #(, #args)*) })); + } + Ok(Some(tokens)) +} + +fn find_error_msg(attrs: &[syn::Attribute]) -> Result<Option<syn::MetaList>, Error> { + let mut error_msg = None; + for attr in attrs { + if let Ok(meta) = attr.parse_meta() { + if meta.path().is_ident("fail") { + if error_msg.is_some() { + return Err(Error::new( + meta.span(), + "Cannot have two display attributes" + )); + } else { + if let syn::Meta::List(list) = meta { + error_msg = Some(list); + } else { + return Err(Error::new( + meta.span(), + "fail attribute must take a list in parentheses" + )); + } + } + } + } + } + Ok(error_msg) +} + +fn is_backtrace(bi: &&synstructure::BindingInfo) -> bool { + match bi.ast().ty { + syn::Type::Path(syn::TypePath { + qself: None, + path: syn::Path { + segments: ref path, .. + }, + }) => path.last().map_or(false, |s| { + s.ident == "Backtrace" && s.arguments.is_empty() + }), + _ => false, + } +} + +fn is_cause(bi: &&synstructure::BindingInfo) -> bool { + let mut found_cause = false; + for attr in &bi.ast().attrs { + if let Ok(meta) = attr.parse_meta() { + if meta.path().is_ident("cause") { + if found_cause { + panic!("Cannot have two `cause` attributes"); + } + found_cause = true; + } + if meta.path().is_ident("fail") { + if let syn::Meta::List(ref list) = meta { + if let Some(ref pair) = list.nested.first() { + if let &&syn::NestedMeta::Meta(syn::Meta::Path(ref path)) = pair { + if path.is_ident("cause") { + if found_cause { + panic!("Cannot have two `cause` attributes"); + } + found_cause = true; + } + } + } + } + } + } + } + found_cause +} diff --git a/third_party/rust/failure_derive/tests/backtrace.rs b/third_party/rust/failure_derive/tests/backtrace.rs new file mode 100644 index 0000000000..a307184112 --- /dev/null +++ b/third_party/rust/failure_derive/tests/backtrace.rs @@ -0,0 +1,64 @@ +extern crate failure; +#[macro_use] +extern crate failure_derive; + +use failure::{Backtrace, Fail}; + +#[derive(Fail, Debug)] +#[fail(display = "Error code: {}", code)] +struct BacktraceError { + backtrace: Backtrace, + code: u32, +} + +#[test] +fn backtrace_error() { + let err = BacktraceError { + backtrace: Backtrace::new(), + code: 7, + }; + let s = format!("{}", err); + assert_eq!(&s[..], "Error code: 7"); + assert!(err.backtrace().is_some()); +} + +#[derive(Fail, Debug)] +#[fail(display = "An error has occurred.")] +struct BacktraceTupleError(Backtrace); + +#[test] +fn backtrace_tuple_error() { + let err = BacktraceTupleError(Backtrace::new()); + let s = format!("{}", err); + assert_eq!(&s[..], "An error has occurred."); + assert!(err.backtrace().is_some()); +} + +#[derive(Fail, Debug)] +enum BacktraceEnumError { + #[fail(display = "Error code: {}", code)] + StructVariant { code: i32, backtrace: Backtrace }, + #[fail(display = "Error: {}", _0)] + TupleVariant(&'static str, Backtrace), + #[fail(display = "An error has occurred.")] + UnitVariant, +} + +#[test] +fn backtrace_enum_error() { + let err = BacktraceEnumError::StructVariant { + code: 2, + backtrace: Backtrace::new(), + }; + let s = format!("{}", err); + assert_eq!(&s[..], "Error code: 2"); + assert!(err.backtrace().is_some()); + let err = BacktraceEnumError::TupleVariant("foobar", Backtrace::new()); + let s = format!("{}", err); + assert_eq!(&s[..], "Error: foobar"); + assert!(err.backtrace().is_some()); + let err = BacktraceEnumError::UnitVariant; + let s = format!("{}", err); + assert_eq!(&s[..], "An error has occurred."); + assert!(err.backtrace().is_none()); +} diff --git a/third_party/rust/failure_derive/tests/custom_type_bounds.rs b/third_party/rust/failure_derive/tests/custom_type_bounds.rs new file mode 100644 index 0000000000..fd1c8b975b --- /dev/null +++ b/third_party/rust/failure_derive/tests/custom_type_bounds.rs @@ -0,0 +1,45 @@ +#[macro_use] +extern crate failure; + +use std::fmt::Debug; + +use failure::Fail; + +#[derive(Debug, Fail)] +#[fail(display = "An error has occurred.")] +pub struct UnboundedGenericTupleError<T: 'static + Debug + Send + Sync>(T); + +#[test] +fn unbounded_generic_tuple_error() { + let s = format!("{}", UnboundedGenericTupleError(())); + assert_eq!(&s[..], "An error has occurred."); +} + +#[derive(Debug, Fail)] +#[fail(display = "An error has occurred: {}", _0)] +pub struct FailBoundsGenericTupleError<T: Fail>(T); + +#[test] +fn fail_bounds_generic_tuple_error() { + let error = FailBoundsGenericTupleError(UnboundedGenericTupleError(())); + let s = format!("{}", error); + assert_eq!(&s[..], "An error has occurred: An error has occurred."); +} + +pub trait NoDisplay: 'static + Debug + Send + Sync {} + +impl NoDisplay for &'static str {} + +#[derive(Debug, Fail)] +#[fail(display = "An error has occurred: {:?}", _0)] +pub struct CustomBoundsGenericTupleError<T: NoDisplay>(T); + +#[test] +fn custom_bounds_generic_tuple_error() { + let error = CustomBoundsGenericTupleError("more details unavailable."); + let s = format!("{}", error); + assert_eq!( + &s[..], + "An error has occurred: \"more details unavailable.\"" + ); +} diff --git a/third_party/rust/failure_derive/tests/no_derive_display.rs b/third_party/rust/failure_derive/tests/no_derive_display.rs new file mode 100644 index 0000000000..20eeb308c2 --- /dev/null +++ b/third_party/rust/failure_derive/tests/no_derive_display.rs @@ -0,0 +1,21 @@ +extern crate failure; +#[macro_use] +extern crate failure_derive; + +use failure::Fail; +use std::fmt::{self, Display}; + +#[derive(Debug, Fail)] +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("An error occurred.") + } +} + +#[test] +fn handwritten_display() { + assert!(Foo.cause().is_none()); + assert_eq!(&format!("{}", Foo)[..], "An error occurred."); +} diff --git a/third_party/rust/failure_derive/tests/tests.rs b/third_party/rust/failure_derive/tests/tests.rs new file mode 100644 index 0000000000..4e73255ef3 --- /dev/null +++ b/third_party/rust/failure_derive/tests/tests.rs @@ -0,0 +1,55 @@ +extern crate failure; +#[macro_use] +extern crate failure_derive; + +#[derive(Fail, Debug)] +#[fail(display = "An error has occurred.")] +struct UnitError; + +#[test] +fn unit_struct() { + let s = format!("{}", UnitError); + assert_eq!(&s[..], "An error has occurred."); +} + +#[derive(Fail, Debug)] +#[fail(display = "Error code: {}", code)] +struct RecordError { + code: u32, +} + +#[test] +fn record_struct() { + let s = format!("{}", RecordError { code: 0 }); + assert_eq!(&s[..], "Error code: 0"); +} + +#[derive(Fail, Debug)] +#[fail(display = "Error code: {}", _0)] +struct TupleError(i32); + +#[test] +fn tuple_struct() { + let s = format!("{}", TupleError(2)); + assert_eq!(&s[..], "Error code: 2"); +} + +#[derive(Fail, Debug)] +enum EnumError { + #[fail(display = "Error code: {}", code)] + StructVariant { code: i32 }, + #[fail(display = "Error: {}", _0)] + TupleVariant(&'static str), + #[fail(display = "An error has occurred.")] + UnitVariant, +} + +#[test] +fn enum_error() { + let s = format!("{}", EnumError::StructVariant { code: 2 }); + assert_eq!(&s[..], "Error code: 2"); + let s = format!("{}", EnumError::TupleVariant("foobar")); + assert_eq!(&s[..], "Error: foobar"); + let s = format!("{}", EnumError::UnitVariant); + assert_eq!(&s[..], "An error has occurred."); +} diff --git a/third_party/rust/failure_derive/tests/wraps.rs b/third_party/rust/failure_derive/tests/wraps.rs new file mode 100644 index 0000000000..9144325cad --- /dev/null +++ b/third_party/rust/failure_derive/tests/wraps.rs @@ -0,0 +1,98 @@ +extern crate failure; +#[macro_use] +extern crate failure_derive; + +use std::fmt; +use std::io; + +use failure::{Backtrace, Fail}; + +#[derive(Fail, Debug)] +#[fail(display = "An error has occurred: {}", inner)] +struct WrapError { + #[fail(cause)] + inner: io::Error, +} + +#[test] +fn wrap_error() { + let inner = io::Error::from_raw_os_error(98); + let err = WrapError { inner }; + assert!(err + .cause() + .and_then(|err| err.downcast_ref::<io::Error>()) + .is_some()); +} + +#[derive(Fail, Debug)] +#[fail(display = "An error has occurred: {}", _0)] +struct WrapTupleError(#[fail(cause)] io::Error); + +#[test] +fn wrap_tuple_error() { + let io_error = io::Error::from_raw_os_error(98); + let err: WrapTupleError = WrapTupleError(io_error); + assert!(err + .cause() + .and_then(|err| err.downcast_ref::<io::Error>()) + .is_some()); +} + +#[derive(Fail, Debug)] +#[fail(display = "An error has occurred: {}", inner)] +struct WrapBacktraceError { + #[fail(cause)] + inner: io::Error, + backtrace: Backtrace, +} + +#[test] +fn wrap_backtrace_error() { + let inner = io::Error::from_raw_os_error(98); + let err: WrapBacktraceError = WrapBacktraceError { + inner, + backtrace: Backtrace::new(), + }; + assert!(err + .cause() + .and_then(|err| err.downcast_ref::<io::Error>()) + .is_some()); + assert!(err.backtrace().is_some()); + assert!(err.backtrace().unwrap().is_empty()); + assert!(err.backtrace().unwrap().to_string().trim().is_empty()); +} + +#[derive(Fail, Debug)] +enum WrapEnumError { + #[fail(display = "An error has occurred: {}", _0)] + Io(#[fail(cause)] io::Error), + #[fail(display = "An error has occurred: {}", inner)] + Fmt { + #[fail(cause)] + inner: fmt::Error, + backtrace: Backtrace, + }, +} + +#[test] +fn wrap_enum_error() { + let io_error = io::Error::from_raw_os_error(98); + let err: WrapEnumError = WrapEnumError::Io(io_error); + assert!(err + .cause() + .and_then(|err| err.downcast_ref::<io::Error>()) + .is_some()); + assert!(err.backtrace().is_none()); + let fmt_error = fmt::Error::default(); + let err: WrapEnumError = WrapEnumError::Fmt { + inner: fmt_error, + backtrace: Backtrace::new(), + }; + assert!(err + .cause() + .and_then(|err| err.downcast_ref::<fmt::Error>()) + .is_some()); + assert!(err.backtrace().is_some()); + assert!(err.backtrace().unwrap().is_empty()); + assert!(err.backtrace().unwrap().to_string().trim().is_empty()); +} |