diff --git a/Cargo.toml b/Cargo.toml index c65cb138..b2a455a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,9 @@ members = ["impl"] [dependencies] derive_more-impl = { version = "=0.99.17", path = "impl" } +[build-dependencies] +rustc_version = { version = "0.4", optional = true } + [dev-dependencies] rustversion = "1.0" trybuild = "1.0.56" @@ -50,7 +53,7 @@ debug = ["derive_more-impl/debug"] deref = ["derive_more-impl/deref"] deref_mut = ["derive_more-impl/deref_mut"] display = ["derive_more-impl/display"] -error = ["derive_more-impl/error", "std"] +error = ["derive_more-impl/error"] from = ["derive_more-impl/from"] from_str = ["derive_more-impl/from_str"] index = ["derive_more-impl/index"] @@ -67,7 +70,7 @@ is_variant = ["derive_more-impl/is_variant"] unwrap = ["derive_more-impl/unwrap"] try_unwrap = ["derive_more-impl/try_unwrap"] -std = [] +std = ["derive_more-impl/std"] full = [ "add_assign", "add", @@ -96,7 +99,7 @@ full = [ "try_unwrap", ] -testing-helpers = ["derive_more-impl/testing-helpers"] +testing-helpers = ["derive_more-impl/testing-helpers", "dep:rustc_version"] [[test]] name = "add_assign" @@ -151,7 +154,7 @@ required-features = ["display"] [[test]] name = "error" path = "tests/error_tests.rs" -required-features = ["error", "std"] +required-features = ["error"] [[test]] name = "from" diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..dbd20bb6 --- /dev/null +++ b/build.rs @@ -0,0 +1,15 @@ +#[cfg(not(feature = "testing-helpers"))] +fn detect_nightly() {} + +#[cfg(feature = "testing-helpers")] +fn detect_nightly() { + use rustc_version::{version_meta, Channel}; + + if version_meta().unwrap().channel == Channel::Nightly { + println!("cargo:rustc-cfg=nightly"); + } +} + +fn main() { + detect_nightly(); +} diff --git a/impl/Cargo.toml b/impl/Cargo.toml index b9054526..86e75857 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -71,4 +71,6 @@ is_variant = ["dep:convert_case"] unwrap = ["dep:convert_case"] try_unwrap = ["dep:convert_case"] +std = [] + testing-helpers = ["dep:rustc_version"] diff --git a/impl/doc/error.md b/impl/doc/error.md index 81e2ac60..5a495bb9 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -42,13 +42,23 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or `#[error(not(source))]`. +### What works in `no_std`? + +If you want to use the `Error` derive on `no_std` environments, then you need to +compile with nightly and enable this feature: +```rust +#![feature(error_in_core)] +``` + +Backtraces don't work though, because the `Backtrace` type is only available in +`std`. ## Example usage ```rust # #![cfg_attr(nightly, feature(error_generic_member_access, provide_any))] -// Nightly requires enabling this features: +// Nightly requires enabling these features: // #![feature(error_generic_member_access, provide_any)] # #[cfg(not(nightly))] fn main() {} # #[cfg(nightly)] fn main() { diff --git a/impl/src/error.rs b/impl/src/error.rs index 3fd10ea4..54650689 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -7,6 +7,16 @@ use crate::utils::{ State, }; +#[cfg(feature = "std")] +fn error_module() -> TokenStream { + quote! { ::std::error } +} + +#[cfg(not(feature = "std"))] +fn error_module() -> TokenStream { + quote! { ::core::error } +} + pub fn expand( input: &syn::DeriveInput, trait_name: &'static str, @@ -15,10 +25,13 @@ pub fn expand( ident, generics, .. } = input; + let error_mod = error_module(); + + let state = State::with_attr_params( input, trait_name, - quote! { ::std::error }, + error_mod.clone(), trait_name.to_lowercase(), allowed_attr_params(), )?; @@ -39,7 +52,7 @@ pub fn expand( let source = source.map(|source| { quote! { - fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { + fn source(&self) -> Option<&(dyn #error_mod::Error + 'static)> { use ::derive_more::__private::AsDynError; #source } @@ -48,7 +61,7 @@ pub fn expand( let provide = provide.map(|provide| { quote! { - fn provide<'_demand>(&'_demand self, demand: &mut ::std::any::Demand<'_demand>) { + fn provide<'_demand>(&'_demand self, demand: &mut ::core::any::Demand<'_demand>) { #provide } } @@ -62,7 +75,7 @@ pub fn expand( &generics, quote! { where - #ident #ty_generics: ::std::fmt::Debug + ::std::fmt::Display + #ident #ty_generics: ::core::fmt::Debug + ::core::fmt::Display }, ); } @@ -73,7 +86,7 @@ pub fn expand( &generics, quote! { where - #(#bounds: ::std::fmt::Debug + ::std::fmt::Display + ::std::error::Error + 'static),* + #(#bounds: ::core::fmt::Debug + ::core::fmt::Display + #error_mod::Error + 'static),* }, ); } @@ -82,7 +95,7 @@ pub fn expand( let render = quote! { #[automatically_derived] - impl #impl_generics ::std::error::Error for #ident #ty_generics #where_clause { + impl #impl_generics #error_mod::Error for #ident #ty_generics #where_clause { #source #provide } @@ -203,11 +216,12 @@ impl<'input, 'state> ParsedFields<'input, 'state> { fn render_provide_as_struct(&self) -> Option { let backtrace = self.backtrace?; + let error_mod = error_module(); let source_provider = self.source.map(|source| { let source_expr = &self.data.members[source]; quote! { - ::std::error::Error::provide(&#source_expr, demand); + #error_mod::Error::provide(&#source_expr, demand); } }); let backtrace_provider = self @@ -217,7 +231,7 @@ impl<'input, 'state> ParsedFields<'input, 'state> { .then(|| { let backtrace_expr = &self.data.members[backtrace]; quote! { - demand.provide_ref::(&#backtrace_expr); + demand.provide_ref::<::std::backtrace::Backtrace>(&#backtrace_expr); } }); @@ -231,13 +245,14 @@ impl<'input, 'state> ParsedFields<'input, 'state> { fn render_provide_as_enum_variant_match_arm(&self) -> Option { let backtrace = self.backtrace?; + let error_mod = error_module(); match self.source { Some(source) if source == backtrace => { let pattern = self.data.matcher(&[source], &[quote! { source }]); Some(quote! { #pattern => { - ::std::error::Error::provide(source, demand); + #error_mod::Error::provide(source, demand); } }) } @@ -248,8 +263,8 @@ impl<'input, 'state> ParsedFields<'input, 'state> { ); Some(quote! { #pattern => { - demand.provide_ref::(backtrace); - ::std::error::Error::provide(source, demand); + demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); + #error_mod::Error::provide(source, demand); } }) } @@ -257,7 +272,7 @@ impl<'input, 'state> ParsedFields<'input, 'state> { let pattern = self.data.matcher(&[backtrace], &[quote! { backtrace }]); Some(quote! { #pattern => { - demand.provide_ref::(backtrace); + demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }) } diff --git a/src/lib.rs b/src/lib.rs index 911b5445..4187c2f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(not(feature = "std"), feature = "error"), feature(error_in_core))] // These links overwrite the ones in `README.md` // to become proper intra-doc links in Rust docs. //! [`From`]: crate::From diff --git a/src/vendor/thiserror/aserror.rs b/src/vendor/thiserror/aserror.rs index 5fea84ef..b4070096 100644 --- a/src/vendor/thiserror/aserror.rs +++ b/src/vendor/thiserror/aserror.rs @@ -1,5 +1,10 @@ +#[cfg(feature = "std")] use std::error::Error; -use std::panic::UnwindSafe; + +#[cfg(not(feature = "std"))] +use core::error::Error; + +use core::panic::UnwindSafe; pub trait AsDynError<'a>: Sealed { fn as_dyn_error(&self) -> &(dyn Error + 'a); diff --git a/tests/error/derives_for_enums_with_source.rs b/tests/error/derives_for_enums_with_source.rs index f1867fa0..6d6c5aaa 100644 --- a/tests/error/derives_for_enums_with_source.rs +++ b/tests/error/derives_for_enums_with_source.rs @@ -12,6 +12,7 @@ enum TestErr { source: SimpleErr, field: i32, }, + #[cfg(std)] NamedImplicitBoxedSource { source: Box, field: i32, @@ -98,6 +99,7 @@ fn named_implicit_source() { assert!(err.source().unwrap().is::()); } +#[cfg(std)] #[test] fn named_implicit_boxed_source() { let err = TestErr::NamedImplicitBoxedSource { diff --git a/tests/error/mod.rs b/tests/error/mod.rs index 6b7785a7..6e9a4809 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -1,5 +1,9 @@ +#[cfg(feature = "std")] use std::error::Error; +#[cfg(not(feature = "std"))] +use core::error::Error; + use derive_more::Error; /// Derives `std::fmt::Display` for structs/enums. @@ -29,17 +33,17 @@ use derive_more::Error; /// ``` macro_rules! derive_display { (@fmt) => { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { write!(f, "") } }; ($type:ident) => { - impl ::std::fmt::Display for $type { + impl ::core::fmt::Display for $type { derive_display!(@fmt); } }; ($type:ident, $($type_parameters:ident),*) => { - impl<$($type_parameters),*> ::std::fmt::Display for $type<$($type_parameters),*> { + impl<$($type_parameters),*> ::core::fmt::Display for $type<$($type_parameters),*> { derive_display!(@fmt); } }; @@ -50,7 +54,7 @@ mod derives_for_generic_enums_with_source; mod derives_for_generic_structs_with_source; mod derives_for_structs_with_source; -#[cfg(nightly)] +#[cfg(all(feature = "std", nightly))] mod nightly; derive_display!(SimpleErr); diff --git a/tests/error_tests.rs b/tests/error_tests.rs index 952a8dd2..28388ac0 100644 --- a/tests/error_tests.rs +++ b/tests/error_tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(nightly, feature(error_generic_member_access, provide_any))] +#![cfg_attr(not(feature = "std"), feature(error_in_core))] mod error; diff --git a/tests/no_std.rs b/tests/no_std.rs index 3b5cdb2d..bdeb2c19 100644 --- a/tests/no_std.rs +++ b/tests/no_std.rs @@ -45,7 +45,7 @@ struct MyBoxedInt<'a>(&'a mut u64); Deref, DerefMut, IntoIterator, - Constructor + Constructor, )] #[deref(forward)] #[deref_mut(forward)]