From 053be64bb68afe3c17f7506c0adb1132a13f1ff7 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Sat, 1 Jul 2023 18:14:21 +0200 Subject: [PATCH 01/11] Support Error in no_std environments The `Error` derive can be made to work well for the most part in `no_std` environments by enabling `#![feature(error_in_core)]`. This changes the `Error` derive slightly to import `Error` and related traits from core, when the `std` feature is disabled. Fixes #261 --- CHANGELOG.md | 2 ++ Cargo.toml | 11 +++--- build.rs | 15 ++++++++ impl/Cargo.toml | 4 ++- impl/doc/error.md | 12 ++++++- impl/src/error.rs | 38 +++++++++++++------- src/lib.rs | 1 + src/vendor/thiserror/aserror.rs | 7 +++- tests/error/derives_for_enums_with_source.rs | 2 ++ tests/error/mod.rs | 12 ++++--- tests/error_tests.rs | 2 ++ 11 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 build.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 10267153..0332d278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). should prevent code style linters from attempting to modify the generated code. - Upgrade to `syn` 2.0. +- The `Error` derive now works in nightly `no_std` environments when enabling + `#![feature(error_in_core)]`. ### Fixed 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..0eec88d4 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -33,7 +33,7 @@ unicode-xid = { version = "0.2.2", optional = true } rustc_version = { version = "0.4", optional = true } [dev-dependencies] -derive_more = { path = "..", features = ["add", "debug", "error", "from_str", "not", "try_into", "try_unwrap"] } +derive_more = { path = "..", features = ["add", "debug", "error", "from_str", "not", "std", "try_into", "try_unwrap"] } itertools = "0.11.0" [badges] @@ -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..af478e59 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,12 @@ 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 +51,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 +60,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 +74,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 +85,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 +94,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 +215,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 +230,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 +244,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 +262,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 +271,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; From 1934137980eb864614d708c3bb9a8a00a389c540 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Sat, 1 Jul 2023 18:14:21 +0200 Subject: [PATCH 02/11] Fix feature tests --- .github/workflows/ci.yml | 4 +++- ci/test_all_features.sh | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e722e6c7..78d812a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,6 +117,8 @@ jobs: test-features: name: test features runs-on: ubuntu-latest + matrix: + std: [std, ''] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 @@ -128,7 +130,7 @@ jobs: go install github.com/pelletier/go-toml/cmd/tomljson@latest echo "$HOME/go/bin" >> $GITHUB_PATH - - run: ci/test_all_features.sh + - run: ci/test_all_features.sh ${{ matrix.std }} diff --git a/ci/test_all_features.sh b/ci/test_all_features.sh index cff8db1f..0c1fd276 100755 --- a/ci/test_all_features.sh +++ b/ci/test_all_features.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -euxo pipefail -for feature in $(tomljson Cargo.toml | jq --raw-output '.features | keys[]' | grep -v 'default\|std\|testing-helpers'); do - cargo test -p derive_more --tests --no-default-features --features "$feature,testing-helpers"; +for feature in $(tomljson Cargo.toml | jq --raw-output '.features | keys[]' | grep -v 'default\|std\|full\|testing-helpers'); do + if [ "${1:-}" = 'std' ]; then + cargo test -p derive_more --tests --no-default-features --features "$feature,std,testing-helpers"; + else + cargo test -p derive_more --tests --no-default-features --features "$feature,testing-helpers"; + fi done From 575ac74cd956177b83f580b1bc997e006fd38fde Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Sun, 2 Jul 2023 12:25:03 +0200 Subject: [PATCH 03/11] Hopefully fix doc tests on stable --- .github/workflows/ci.yml | 6 ++++-- impl/doc/error.md | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78d812a9..9ed36bba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,8 +117,10 @@ jobs: test-features: name: test features runs-on: ubuntu-latest - matrix: - std: [std, ''] + strategy: + fail-fast: false + matrix: + std: [std, ''] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 diff --git a/impl/doc/error.md b/impl/doc/error.md index 5a495bb9..5ca38b41 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -47,6 +47,7 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or If you want to use the `Error` derive on `no_std` environments, then you need to compile with nightly and enable this feature: ```rust +# #[cfg(nightly)] #![feature(error_in_core)] ``` From d9d71b2ab9c15ad43d0f14f30ed895453de522c8 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Sun, 2 Jul 2023 23:46:44 +0200 Subject: [PATCH 04/11] cargo fmt --- impl/src/fmt/display.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index 0e9983a5..7eb893e4 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -331,12 +331,16 @@ impl<'a> Expansion<'a> { /// Generates trait bounds for a struct or an enum variant. fn generate_bounds(&self) -> Vec { let Some(fmt) = &self.attrs.fmt else { - return self.fields.iter().next().map(|f| { - let ty = &f.ty; - let trait_ident = &self.trait_ident; - vec![parse_quote! { #ty: ::core::fmt::#trait_ident }] - }) - .unwrap_or_default(); + return self + .fields + .iter() + .next() + .map(|f| { + let ty = &f.ty; + let trait_ident = &self.trait_ident; + vec![parse_quote! { #ty: ::core::fmt::#trait_ident }] + }) + .unwrap_or_default(); }; fmt.bounded_types(self.fields) From 079cbb7a9128bec3913847b9ce4c692cc94391c9 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Sun, 2 Jul 2023 23:46:44 +0200 Subject: [PATCH 05/11] Fix doc test --- impl/doc/error.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/impl/doc/error.md b/impl/doc/error.md index 5ca38b41..26ab3d72 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -46,8 +46,7 @@ ignored for one of these methods by using `#[error(not(backtrace))]` or If you want to use the `Error` derive on `no_std` environments, then you need to compile with nightly and enable this feature: -```rust -# #[cfg(nightly)] +```ignore #![feature(error_in_core)] ``` From 8ce404c4e42ae6cb96d3072cb5500f7fadfe750b Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 3 Jul 2023 00:13:40 +0200 Subject: [PATCH 06/11] Re-export Error from the derive_more crate --- impl/src/error.rs | 28 +++++++--------------------- src/lib.rs | 6 ++++++ tests/error/mod.rs | 6 ------ 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/impl/src/error.rs b/impl/src/error.rs index af478e59..fb53960f 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -7,16 +7,6 @@ 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, @@ -25,12 +15,10 @@ pub fn expand( ident, generics, .. } = input; - let error_mod = error_module(); - let state = State::with_attr_params( input, trait_name, - error_mod.clone(), + quote!{ ::derive_more::Error }, trait_name.to_lowercase(), allowed_attr_params(), )?; @@ -51,7 +39,7 @@ pub fn expand( let source = source.map(|source| { quote! { - fn source(&self) -> Option<&(dyn #error_mod::Error + 'static)> { + fn source(&self) -> Option<&(dyn ::derive_more::Error + 'static)> { use ::derive_more::__private::AsDynError; #source } @@ -85,7 +73,7 @@ pub fn expand( &generics, quote! { where - #(#bounds: ::core::fmt::Debug + ::core::fmt::Display + #error_mod::Error + 'static),* + #(#bounds: ::core::fmt::Debug + ::core::fmt::Display + ::derive_more::Error + 'static),* }, ); } @@ -94,7 +82,7 @@ pub fn expand( let render = quote! { #[automatically_derived] - impl #impl_generics #error_mod::Error for #ident #ty_generics #where_clause { + impl #impl_generics ::derive_more::Error for #ident #ty_generics #where_clause { #source #provide } @@ -215,12 +203,11 @@ 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! { - #error_mod::Error::provide(&#source_expr, demand); + ::derive_more::Error::provide(&#source_expr, demand); } }); let backtrace_provider = self @@ -244,14 +231,13 @@ 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 => { - #error_mod::Error::provide(source, demand); + ::derive_more::Error::provide(source, demand); } }) } @@ -263,7 +249,7 @@ impl<'input, 'state> ParsedFields<'input, 'state> { Some(quote! { #pattern => { demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); - #error_mod::Error::provide(source, demand); + ::derive_more::Error::provide(source, demand); } }) } diff --git a/src/lib.rs b/src/lib.rs index 4187c2f6..3753bf65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,12 @@ pub use self::r#str::FromStrError; #[cfg(feature = "error")] mod vendor; +#[cfg(all(feature = "std", feature = "error"))] +pub use ::std::error::Error; + +#[cfg(all(not(feature = "std"), feature = "error"))] +pub use ::core::error::Error; + // Not public API. #[doc(hidden)] #[cfg(feature = "error")] diff --git a/tests/error/mod.rs b/tests/error/mod.rs index 6e9a4809..352d40aa 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -1,9 +1,3 @@ -#[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. From a38e5d54c55dafdb88dad09d5cf6373838f072b8 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 3 Jul 2023 00:23:26 +0200 Subject: [PATCH 07/11] Make Error type private --- impl/src/error.rs | 14 +++++++------- src/lib.rs | 10 +++++----- tests/error/mod.rs | 6 ++++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/impl/src/error.rs b/impl/src/error.rs index fb53960f..b7126ed5 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -18,7 +18,7 @@ pub fn expand( let state = State::with_attr_params( input, trait_name, - quote!{ ::derive_more::Error }, + quote!{ ::derive_more::__private::Error }, trait_name.to_lowercase(), allowed_attr_params(), )?; @@ -39,7 +39,7 @@ pub fn expand( let source = source.map(|source| { quote! { - fn source(&self) -> Option<&(dyn ::derive_more::Error + 'static)> { + fn source(&self) -> Option<&(dyn ::derive_more::__private::Error + 'static)> { use ::derive_more::__private::AsDynError; #source } @@ -73,7 +73,7 @@ pub fn expand( &generics, quote! { where - #(#bounds: ::core::fmt::Debug + ::core::fmt::Display + ::derive_more::Error + 'static),* + #(#bounds: ::core::fmt::Debug + ::core::fmt::Display + ::derive_more::__private::Error + 'static),* }, ); } @@ -82,7 +82,7 @@ pub fn expand( let render = quote! { #[automatically_derived] - impl #impl_generics ::derive_more::Error for #ident #ty_generics #where_clause { + impl #impl_generics ::derive_more::__private::Error for #ident #ty_generics #where_clause { #source #provide } @@ -207,7 +207,7 @@ impl<'input, 'state> ParsedFields<'input, 'state> { let source_provider = self.source.map(|source| { let source_expr = &self.data.members[source]; quote! { - ::derive_more::Error::provide(&#source_expr, demand); + ::derive_more::__private::Error::provide(&#source_expr, demand); } }); let backtrace_provider = self @@ -237,7 +237,7 @@ impl<'input, 'state> ParsedFields<'input, 'state> { let pattern = self.data.matcher(&[source], &[quote! { source }]); Some(quote! { #pattern => { - ::derive_more::Error::provide(source, demand); + ::derive_more::__private::Error::provide(source, demand); } }) } @@ -249,7 +249,7 @@ impl<'input, 'state> ParsedFields<'input, 'state> { Some(quote! { #pattern => { demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); - ::derive_more::Error::provide(source, demand); + ::derive_more::__private::Error::provide(source, demand); } }) } diff --git a/src/lib.rs b/src/lib.rs index 3753bf65..8674781d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,16 +74,16 @@ pub use self::r#str::FromStrError; #[cfg(feature = "error")] mod vendor; -#[cfg(all(feature = "std", feature = "error"))] -pub use ::std::error::Error; - -#[cfg(all(not(feature = "std"), feature = "error"))] -pub use ::core::error::Error; // Not public API. #[doc(hidden)] #[cfg(feature = "error")] pub mod __private { + #[cfg(feature = "std")] + pub use ::std::error::Error; + + #[cfg(not(feature = "std"))] + pub use ::core::error::Error; pub use crate::vendor::thiserror::aserror::AsDynError; } diff --git a/tests/error/mod.rs b/tests/error/mod.rs index 352d40aa..6e9a4809 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -1,3 +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. From 1965ae486aab98e8fd14a9e27a86145c7d4a66d3 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 3 Jul 2023 18:57:57 +0300 Subject: [PATCH 08/11] Minor corrections [skip ci] --- .github/workflows/ci.yml | 7 ++----- impl/doc/error.md | 2 ++ impl/src/error.rs | 2 +- impl/src/fmt/display.rs | 16 ++++++---------- src/lib.rs | 5 ++--- src/vendor/thiserror/aserror.rs | 1 - tests/error/mod.rs | 1 - 7 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ed36bba..2a5c7a8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,10 +117,6 @@ jobs: test-features: name: test features runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - std: [std, ''] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 @@ -132,7 +128,8 @@ jobs: go install github.com/pelletier/go-toml/cmd/tomljson@latest echo "$HOME/go/bin" >> $GITHUB_PATH - - run: ci/test_all_features.sh ${{ matrix.std }} + - run: ci/test_all_features.sh + - run: ci/test_all_features.sh std diff --git a/impl/doc/error.md b/impl/doc/error.md index 26ab3d72..20ff24cf 100644 --- a/impl/doc/error.md +++ b/impl/doc/error.md @@ -54,6 +54,8 @@ Backtraces don't work though, because the `Backtrace` type is only available in `std`. + + ## Example usage ```rust diff --git a/impl/src/error.rs b/impl/src/error.rs index b7126ed5..4c4ba983 100644 --- a/impl/src/error.rs +++ b/impl/src/error.rs @@ -18,7 +18,7 @@ pub fn expand( let state = State::with_attr_params( input, trait_name, - quote!{ ::derive_more::__private::Error }, + quote! { ::derive_more::__private::Error }, trait_name.to_lowercase(), allowed_attr_params(), )?; diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index 7eb893e4..0e9983a5 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -331,16 +331,12 @@ impl<'a> Expansion<'a> { /// Generates trait bounds for a struct or an enum variant. fn generate_bounds(&self) -> Vec { let Some(fmt) = &self.attrs.fmt else { - return self - .fields - .iter() - .next() - .map(|f| { - let ty = &f.ty; - let trait_ident = &self.trait_ident; - vec![parse_quote! { #ty: ::core::fmt::#trait_ident }] - }) - .unwrap_or_default(); + return self.fields.iter().next().map(|f| { + let ty = &f.ty; + let trait_ident = &self.trait_ident; + vec![parse_quote! { #ty: ::core::fmt::#trait_ident }] + }) + .unwrap_or_default(); }; fmt.bounded_types(self.fields) diff --git a/src/lib.rs b/src/lib.rs index 8674781d..82ced1e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,16 +74,15 @@ pub use self::r#str::FromStrError; #[cfg(feature = "error")] mod vendor; - // Not public API. #[doc(hidden)] #[cfg(feature = "error")] pub mod __private { + #[cfg(not(feature = "std"))] + pub use ::core::error::Error; #[cfg(feature = "std")] pub use ::std::error::Error; - #[cfg(not(feature = "std"))] - pub use ::core::error::Error; pub use crate::vendor::thiserror::aserror::AsDynError; } diff --git a/src/vendor/thiserror/aserror.rs b/src/vendor/thiserror/aserror.rs index b4070096..72479ec4 100644 --- a/src/vendor/thiserror/aserror.rs +++ b/src/vendor/thiserror/aserror.rs @@ -1,6 +1,5 @@ #[cfg(feature = "std")] use std::error::Error; - #[cfg(not(feature = "std"))] use core::error::Error; diff --git a/tests/error/mod.rs b/tests/error/mod.rs index 6e9a4809..964830bd 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -1,6 +1,5 @@ #[cfg(feature = "std")] use std::error::Error; - #[cfg(not(feature = "std"))] use core::error::Error; From 06cbb9101453b8353dee8441589642be3179a165 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 3 Jul 2023 22:15:38 +0200 Subject: [PATCH 09/11] Address review --- .github/workflows/ci.yml | 7 +++++-- Cargo.toml | 2 +- impl/Cargo.toml | 2 -- tests/error/derives_for_enums_with_source.rs | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a5c7a8a..48a4fa9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,6 +117,10 @@ jobs: test-features: name: test features runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + std: [std, no_std] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 @@ -128,8 +132,7 @@ jobs: go install github.com/pelletier/go-toml/cmd/tomljson@latest echo "$HOME/go/bin" >> $GITHUB_PATH - - run: ci/test_all_features.sh - - run: ci/test_all_features.sh std + - run: ci/test_all_features.sh ${{ matrix.std }} diff --git a/Cargo.toml b/Cargo.toml index b2a455a6..7ad2a175 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ is_variant = ["derive_more-impl/is_variant"] unwrap = ["derive_more-impl/unwrap"] try_unwrap = ["derive_more-impl/try_unwrap"] -std = ["derive_more-impl/std"] +std = [] full = [ "add_assign", "add", diff --git a/impl/Cargo.toml b/impl/Cargo.toml index 0eec88d4..51719c39 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -71,6 +71,4 @@ is_variant = ["dep:convert_case"] unwrap = ["dep:convert_case"] try_unwrap = ["dep:convert_case"] -std = [] - testing-helpers = ["dep:rustc_version"] diff --git a/tests/error/derives_for_enums_with_source.rs b/tests/error/derives_for_enums_with_source.rs index 6d6c5aaa..d8e5db7d 100644 --- a/tests/error/derives_for_enums_with_source.rs +++ b/tests/error/derives_for_enums_with_source.rs @@ -12,7 +12,7 @@ enum TestErr { source: SimpleErr, field: i32, }, - #[cfg(std)] + #[cfg(feature = "std")] NamedImplicitBoxedSource { source: Box, field: i32, @@ -99,7 +99,7 @@ fn named_implicit_source() { assert!(err.source().unwrap().is::()); } -#[cfg(std)] +#[cfg(feature = "std")] #[test] fn named_implicit_boxed_source() { let err = TestErr::NamedImplicitBoxedSource { From 1a8d07a58512c1a0d98d0712b80dd59fd6bffe75 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 3 Jul 2023 22:24:19 +0200 Subject: [PATCH 10/11] rustfmt --- impl/src/fmt/display.rs | 16 ++++++++++------ src/vendor/thiserror/aserror.rs | 4 ++-- tests/error/mod.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index 0e9983a5..7eb893e4 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -331,12 +331,16 @@ impl<'a> Expansion<'a> { /// Generates trait bounds for a struct or an enum variant. fn generate_bounds(&self) -> Vec { let Some(fmt) = &self.attrs.fmt else { - return self.fields.iter().next().map(|f| { - let ty = &f.ty; - let trait_ident = &self.trait_ident; - vec![parse_quote! { #ty: ::core::fmt::#trait_ident }] - }) - .unwrap_or_default(); + return self + .fields + .iter() + .next() + .map(|f| { + let ty = &f.ty; + let trait_ident = &self.trait_ident; + vec![parse_quote! { #ty: ::core::fmt::#trait_ident }] + }) + .unwrap_or_default(); }; fmt.bounded_types(self.fields) diff --git a/src/vendor/thiserror/aserror.rs b/src/vendor/thiserror/aserror.rs index 72479ec4..c3e086f0 100644 --- a/src/vendor/thiserror/aserror.rs +++ b/src/vendor/thiserror/aserror.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "std")] -use std::error::Error; #[cfg(not(feature = "std"))] use core::error::Error; +#[cfg(feature = "std")] +use std::error::Error; use core::panic::UnwindSafe; diff --git a/tests/error/mod.rs b/tests/error/mod.rs index 964830bd..a041ec17 100644 --- a/tests/error/mod.rs +++ b/tests/error/mod.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "std")] -use std::error::Error; #[cfg(not(feature = "std"))] use core::error::Error; +#[cfg(feature = "std")] +use std::error::Error; use derive_more::Error; From cc40c3b266b8076e3f0cfbb1cc3ba6e597d786fb Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 5 Jul 2023 14:28:55 +0300 Subject: [PATCH 11/11] Minor corrections --- .github/workflows/ci.yml | 4 ++-- ci/test_all_features.sh | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48a4fa9c..68dd4978 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,11 +116,11 @@ jobs: test-features: name: test features - runs-on: ubuntu-latest strategy: fail-fast: false matrix: - std: [std, no_std] + std: ["std", "no_std"] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@v1 diff --git a/ci/test_all_features.sh b/ci/test_all_features.sh index 0c1fd276..9357d570 100755 --- a/ci/test_all_features.sh +++ b/ci/test_all_features.sh @@ -1,10 +1,12 @@ #!/usr/bin/env bash + +std='' +if [ "${1:-}" = 'std' ]; then + std=',std' +fi + set -euxo pipefail for feature in $(tomljson Cargo.toml | jq --raw-output '.features | keys[]' | grep -v 'default\|std\|full\|testing-helpers'); do - if [ "${1:-}" = 'std' ]; then - cargo test -p derive_more --tests --no-default-features --features "$feature,std,testing-helpers"; - else - cargo test -p derive_more --tests --no-default-features --features "$feature,testing-helpers"; - fi + cargo +nightly test -p derive_more --tests --no-default-features --features "$feature$std,testing-helpers" done