diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65a20f51..69e489a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,9 @@ jobs: - name: Enable nightly-only tests run: echo RUSTFLAGS=${RUSTFLAGS}\ --cfg=thiserror_nightly_testing >> $GITHUB_ENV if: matrix.rust == 'nightly' + - name: Test no-default-features + run: cargo test --all --no-default-features + if: matrix.rust != '1.56.0' - run: cargo test --all - uses: actions/upload-artifact@v4 if: matrix.rust == 'nightly' && always() diff --git a/Cargo.toml b/Cargo.toml index 1d1f8b05..aefbdc61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,8 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/thiserror" rust-version = "1.56" +# without the `std` feature (i.e. `no_std`): rust-version = "1.81" + [dependencies] thiserror-impl = { version = "=1.0.64", path = "impl" } @@ -26,3 +28,11 @@ members = ["impl"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] + +[features] +std = [] +default = ["std"] + +[[test]] +name = "test_path" +required-features = ["std"] diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 403cd07d..a90abc58 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -37,7 +37,7 @@ fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream { #[allow(unused_qualifications)] #[automatically_derived] - impl #impl_generics std::error::Error for #ty #ty_generics #where_clause + impl #impl_generics ::thiserror::__private::error::Error for #ty #ty_generics #where_clause where // Work around trivial bounds being unstable. // https://github.com/rust-lang/rust/issues/48214 @@ -62,17 +62,19 @@ fn impl_struct(input: Struct) -> TokenStream { let source_body = if let Some(transparent_attr) = &input.attrs.transparent { let only_field = &input.fields[0]; if only_field.contains_generic { - error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error)); + error_inferred_bounds + .insert(only_field.ty, quote!(::thiserror::__private::error::Error)); } let member = &only_field.member; Some(quote_spanned! {transparent_attr.span=> - std::error::Error::source(self.#member.as_dyn_error()) + ::thiserror::__private::error::Error::source(self.#member.as_dyn_error()) }) } else if let Some(source_field) = input.source_field() { let source = &source_field.member; if source_field.contains_generic { let ty = unoptional_type(source_field.ty); - error_inferred_bounds.insert(ty, quote!(std::error::Error + 'static)); + error_inferred_bounds + .insert(ty, quote!(::thiserror::__private::error::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.member_span()=> .as_ref()?)) @@ -90,8 +92,8 @@ fn impl_struct(input: Struct) -> TokenStream { }; let source_method = source_body.map(|body| { quote! { - fn source(&self) -> ::core::option::Option<&(dyn std::error::Error + 'static)> { - use thiserror::__private::AsDynError as _; + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::error::Error + 'static)> { + use ::thiserror::__private::AsDynError as _; #body } } @@ -118,32 +120,32 @@ fn impl_struct(input: Struct) -> TokenStream { } else if type_is_option(backtrace_field.ty) { Some(quote! { if let ::core::option::Option::Some(backtrace) = &self.#backtrace { - #request.provide_ref::(backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }) } else { Some(quote! { - #request.provide_ref::(&self.#backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(&self.#backtrace); }) }; quote! { - use thiserror::__private::ThiserrorProvide as _; + use ::thiserror::__private::ThiserrorProvide as _; #source_provide #self_provide } } else if type_is_option(backtrace_field.ty) { quote! { if let ::core::option::Option::Some(backtrace) = &self.#backtrace { - #request.provide_ref::(backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(backtrace); } } } else { quote! { - #request.provide_ref::(&self.#backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(&self.#backtrace); } }; quote! { - fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) { + fn provide<'_request>(&'_request self, #request: &mut ::thiserror::__private::error::Request<'_request>) { #body } } @@ -216,7 +218,7 @@ fn impl_struct(input: Struct) -> TokenStream { quote! { #[allow(unused_qualifications)] #[automatically_derived] - impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause { + impl #impl_generics ::thiserror::__private::error::Error for #ty #ty_generics #error_where_clause { #source_method #provide_method } @@ -236,11 +238,11 @@ fn impl_enum(input: Enum) -> TokenStream { if let Some(transparent_attr) = &variant.attrs.transparent { let only_field = &variant.fields[0]; if only_field.contains_generic { - error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error)); + error_inferred_bounds.insert(only_field.ty, quote!(::thiserror::__private::error::Error)); } let member = &only_field.member; let source = quote_spanned! {transparent_attr.span=> - std::error::Error::source(transparent.as_dyn_error()) + ::thiserror::__private::error::Error::source(transparent.as_dyn_error()) }; quote! { #ty::#ident {#member: transparent} => #source, @@ -249,7 +251,7 @@ fn impl_enum(input: Enum) -> TokenStream { let source = &source_field.member; if source_field.contains_generic { let ty = unoptional_type(source_field.ty); - error_inferred_bounds.insert(ty, quote!(std::error::Error + 'static)); + error_inferred_bounds.insert(ty, quote!(::thiserror::__private::error::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.member_span()=> .as_ref()?)) @@ -270,8 +272,8 @@ fn impl_enum(input: Enum) -> TokenStream { } }); Some(quote! { - fn source(&self) -> ::core::option::Option<&(dyn std::error::Error + 'static)> { - use thiserror::__private::AsDynError as _; + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::error::Error + 'static)> { + use ::thiserror::__private::AsDynError as _; #[allow(deprecated)] match self { #(#arms)* @@ -307,12 +309,12 @@ fn impl_enum(input: Enum) -> TokenStream { let self_provide = if type_is_option(backtrace_field.ty) { quote! { if let ::core::option::Option::Some(backtrace) = backtrace { - #request.provide_ref::(backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(backtrace); } } } else { quote! { - #request.provide_ref::(backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }; quote! { @@ -321,7 +323,7 @@ fn impl_enum(input: Enum) -> TokenStream { #source: #varsource, .. } => { - use thiserror::__private::ThiserrorProvide as _; + use ::thiserror::__private::ThiserrorProvide as _; #source_provide #self_provide } @@ -345,7 +347,7 @@ fn impl_enum(input: Enum) -> TokenStream { }; quote! { #ty::#ident {#backtrace: #varsource, ..} => { - use thiserror::__private::ThiserrorProvide as _; + use ::thiserror::__private::ThiserrorProvide as _; #source_provide } } @@ -355,12 +357,12 @@ fn impl_enum(input: Enum) -> TokenStream { let body = if type_is_option(backtrace_field.ty) { quote! { if let ::core::option::Option::Some(backtrace) = backtrace { - #request.provide_ref::(backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(backtrace); } } } else { quote! { - #request.provide_ref::(backtrace); + #request.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }; quote! { @@ -375,7 +377,7 @@ fn impl_enum(input: Enum) -> TokenStream { } }); Some(quote! { - fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) { + fn provide<'_request>(&'_request self, #request: &mut ::thiserror::__private::error::Request<'_request>) { #[allow(deprecated)] match self { #(#arms)* @@ -475,7 +477,7 @@ fn impl_enum(input: Enum) -> TokenStream { quote! { #[allow(unused_qualifications)] #[automatically_derived] - impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause { + impl #impl_generics ::thiserror::__private::error::Error for #ty #ty_generics #error_where_clause { #source_method #provide_method } @@ -502,7 +504,7 @@ fn fields_pat(fields: &[Field]) -> TokenStream { fn use_as_display(needs_as_display: bool) -> Option { if needs_as_display { Some(quote! { - use thiserror::__private::AsDisplay as _; + use ::thiserror::__private::AsDisplay as _; }) } else { None @@ -520,11 +522,11 @@ fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> Toke let backtrace_member = &backtrace_field.member; if type_is_option(backtrace_field.ty) { quote! { - #backtrace_member: ::core::option::Option::Some(std::backtrace::Backtrace::capture()), + #backtrace_member: ::core::option::Option::Some(::std::backtrace::Backtrace::capture()), } } else { quote! { - #backtrace_member: ::core::convert::From::from(std::backtrace::Backtrace::capture()), + #backtrace_member: ::core::convert::From::from(::std::backtrace::Backtrace::capture()), } } }); diff --git a/src/aserror.rs b/src/aserror.rs index 11cb4d99..a55da383 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,5 +1,5 @@ +use crate::__private::error::Error; use core::panic::UnwindSafe; -use std::error::Error; #[doc(hidden)] pub trait AsDynError<'a>: Sealed { diff --git a/src/display.rs b/src/display.rs index 3c43216a..24bea715 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,4 +1,5 @@ use core::fmt::Display; +#[cfg(feature = "std")] use std::path::{self, Path, PathBuf}; #[doc(hidden)] @@ -21,6 +22,7 @@ where } } +#[cfg(feature = "std")] impl<'a> AsDisplay<'a> for Path { type Target = path::Display<'a>; @@ -30,6 +32,7 @@ impl<'a> AsDisplay<'a> for Path { } } +#[cfg(feature = "std")] impl<'a> AsDisplay<'a> for PathBuf { type Target = path::Display<'a>; diff --git a/src/lib.rs b/src/lib.rs index 92d3c5dd..eb46547a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,6 +266,7 @@ clippy::wildcard_imports )] #![cfg_attr(error_generic_member_access, feature(error_generic_member_access))] +#![cfg_attr(not(feature = "std"), no_std)] #[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))] compile_error!("Build script probe failed to compile."); @@ -287,4 +288,10 @@ pub mod __private { #[cfg(error_generic_member_access)] #[doc(hidden)] pub use crate::provide::ThiserrorProvide; + #[cfg(not(feature = "std"))] + #[doc(hidden)] + pub use core::error; + #[cfg(feature = "std")] + #[doc(hidden)] + pub use std::error; } diff --git a/src/provide.rs b/src/provide.rs index 7b4e9223..45151d36 100644 --- a/src/provide.rs +++ b/src/provide.rs @@ -1,4 +1,4 @@ -use std::error::{Error, Request}; +use crate::__private::error::{Error, Request}; #[doc(hidden)] pub trait ThiserrorProvide: Sealed { diff --git a/tests/test_no_std.rs b/tests/test_no_std.rs new file mode 100644 index 00000000..0cb28528 --- /dev/null +++ b/tests/test_no_std.rs @@ -0,0 +1,23 @@ +#![no_std] +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("io")] +pub struct IoError; + +#[derive(Error, Debug)] +pub enum MyError { + #[error("A")] + A, + #[error("B {0}")] + B(#[from] IoError), +} + +#[test] +#[cfg(not(feature = "std"))] +fn test_no_std() { + use core::error::Error as _; + + let error = MyError::from(IoError); + error.source().unwrap().downcast_ref::().unwrap(); +}