From 6085f29e604dda4a6a741991256c493dc74b6402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 16 May 2024 15:12:12 +0200 Subject: [PATCH 01/24] support no_std with error-in-core --- Cargo.toml | 4 ++++ impl/src/expand.rs | 20 ++++++++++---------- src/aserror.rs | 4 ++-- src/display.rs | 5 ++++- src/lib.rs | 13 +++++++++++-- src/provide.rs | 2 +- tests/test_backtrace.rs | 1 + tests/test_deprecated.rs | 3 ++- tests/test_display.rs | 1 + tests/test_error.rs | 1 + tests/test_expr.rs | 1 + tests/test_from.rs | 1 + tests/test_generics.rs | 1 + tests/test_lints.rs | 3 ++- tests/test_path.rs | 2 ++ tests/test_source.rs | 2 ++ tests/test_transparent.rs | 18 +++++++++++------- 17 files changed, 57 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd2d13d..2ce5abc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,7 @@ members = ["impl"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] + +[features] +std = [] +default = ["std"] diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 296b567..ce6a0c3 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -36,7 +36,7 @@ fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream { #error #[allow(unused_qualifications)] - impl #impl_generics std::error::Error for #ty #ty_generics #where_clause + impl #impl_generics ::thiserror::error::Error for #ty #ty_generics #where_clause where // Work around trivial bounds being unstable. // https://github.com/rust-lang/rust/issues/48214 @@ -60,17 +60,17 @@ 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::error::Error)); } let member = &only_field.member; Some(quote_spanned! {transparent_attr.span=> - std::error::Error::source(self.#member.as_dyn_error()) + ::thiserror::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::error::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.member_span()=> .as_ref()?)) @@ -88,7 +88,7 @@ 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)> { + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::error::Error + 'static)> { use thiserror::__private::AsDynError as _; #body } @@ -211,7 +211,7 @@ fn impl_struct(input: Struct) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause { + impl #impl_generics ::thiserror::error::Error for #ty #ty_generics #error_where_clause { #source_method #provide_method } @@ -231,11 +231,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::error::Error)); } let member = &only_field.member; let source = quote_spanned! {transparent_attr.span=> - std::error::Error::source(transparent.as_dyn_error()) + ::thiserror::error::Error::source(transparent.as_dyn_error()) }; quote! { #ty::#ident {#member: transparent} => #source, @@ -244,7 +244,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::error::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.member_span()=> .as_ref()?)) @@ -467,7 +467,7 @@ fn impl_enum(input: Enum) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics std::error::Error for #ty #ty_generics #error_where_clause { + impl #impl_generics ::thiserror::error::Error for #ty #ty_generics #error_where_clause { #source_method #provide_method } diff --git a/src/aserror.rs b/src/aserror.rs index 54fc6f1..5c4abdb 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,5 +1,5 @@ -use std::error::Error; -use std::panic::UnwindSafe; +use crate::error::Error; +use core::panic::UnwindSafe; #[doc(hidden)] pub trait AsDynError<'a>: Sealed { diff --git a/src/display.rs b/src/display.rs index 27098f1..24bea71 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,4 +1,5 @@ -use std::fmt::Display; +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 81a04b0..6a281ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,7 @@ //! # use thiserror::Error; //! # //! #[derive(Error, Debug)] -//! pub enum Error { +//! pub enum MyError { //! #[error("invalid rdo_lookahead_frames {0} (expected < {})", i32::MAX)] //! InvalidLookahead(u32), //! } @@ -90,7 +90,7 @@ //! # } //! # //! #[derive(Error, Debug)] -//! pub enum Error { +//! pub enum MyError { //! #[error("first letter must be lowercase but was {:?}", first_char(.0))] //! WrongCase(String), //! #[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)] @@ -236,6 +236,7 @@ clippy::wildcard_imports )] #![cfg_attr(error_generic_member_access, feature(error_generic_member_access))] +#![cfg_attr(not(feature = "std"), no_std, feature(error_in_core))] #[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))] compile_error!("Build script probe failed to compile."); @@ -245,6 +246,14 @@ mod display; #[cfg(error_generic_member_access)] mod provide; +#[cfg(feature = "std")] +#[doc(hidden)] +pub use std::error; + +#[cfg(not(feature = "std"))] +#[doc(hidden)] +pub use core::error; + pub use thiserror_impl::*; // Not public API. diff --git a/src/provide.rs b/src/provide.rs index 7b4e922..f82a041 100644 --- a/src/provide.rs +++ b/src/provide.rs @@ -1,4 +1,4 @@ -use std::error::{Error, Request}; +use crate::error::{Error, Request}; #[doc(hidden)] pub trait ThiserrorProvide: Sealed { diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 4710d45..d2d0e27 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))] use thiserror::Error; diff --git a/tests/test_deprecated.rs b/tests/test_deprecated.rs index 5524666..5fc9210 100644 --- a/tests/test_deprecated.rs +++ b/tests/test_deprecated.rs @@ -1,9 +1,10 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![deny(deprecated, clippy::all, clippy::pedantic)] use thiserror::Error; #[derive(Error, Debug)] -pub enum Error { +pub enum MyError { #[deprecated] #[error("...")] Deprecated, diff --git a/tests/test_display.rs b/tests/test_display.rs index 95a210f..5b87975 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_raw_string_hashes, clippy::uninlined_format_args)] use std::fmt::{self, Display}; diff --git a/tests/test_error.rs b/tests/test_error.rs index fab934d..dd5c836 100644 --- a/tests/test_error.rs +++ b/tests/test_error.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(dead_code)] use std::fmt::{self, Display}; diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 028f34e..c5d850d 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::iter_cloned_collect, clippy::uninlined_format_args)] use std::fmt::Display; diff --git a/tests/test_from.rs b/tests/test_from.rs index 51af40b..f1b34a4 100644 --- a/tests/test_from.rs +++ b/tests/test_from.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::extra_unused_type_parameters)] use std::io; diff --git a/tests/test_generics.rs b/tests/test_generics.rs index c94d95e..a6036f7 100644 --- a/tests/test_generics.rs +++ b/tests/test_generics.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_late_init, clippy::uninlined_format_args)] use std::fmt::{self, Debug, Display}; diff --git a/tests/test_lints.rs b/tests/test_lints.rs index cafcbc0..ac0a3fb 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -1,8 +1,9 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::mixed_attributes_style)] use thiserror::Error; -pub use std::error::Error; +pub use thiserror::error::Error; #[test] fn test_unused_qualifications() { diff --git a/tests/test_path.rs b/tests/test_path.rs index a34a3d7..9e97736 100644 --- a/tests/test_path.rs +++ b/tests/test_path.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "std")] + use ref_cast::RefCast; use std::fmt::Display; use std::path::{Path, PathBuf}; diff --git a/tests/test_source.rs b/tests/test_source.rs index 637f4ac..a93fec3 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] + use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 6f3c03e..89153dc 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,10 +1,14 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] + use anyhow::anyhow; -use std::error::Error as _; -use std::io; +use thiserror::error::Error; use thiserror::Error; +#[cfg(feature = "std")] #[test] fn test_transparent_struct() { + use std::io; + #[derive(Error, Debug)] #[error(transparent)] struct Error(ErrorKind); @@ -30,17 +34,17 @@ fn test_transparent_struct() { #[test] fn test_transparent_enum() { #[derive(Error, Debug)] - enum Error { + enum MyError { #[error("this failed")] This, #[error(transparent)] Other(anyhow::Error), } - let error = Error::This; + let error = MyError::This; assert_eq!("this failed", error.to_string()); - let error = Error::Other(anyhow!("inner").context("outer")); + let error = MyError::Other(anyhow!("inner").context("outer")); assert_eq!("outer", error.to_string()); assert_eq!("inner", error.source().unwrap().to_string()); } @@ -60,7 +64,7 @@ fn test_anyhow() { fn test_non_static() { #[derive(Error, Debug)] #[error(transparent)] - struct Error<'a> { + struct MyError<'a> { inner: ErrorKind<'a>, } @@ -70,7 +74,7 @@ fn test_non_static() { Unexpected { token: &'a str }, } - let error = Error { + let error = MyError { inner: ErrorKind::Unexpected { token: "error" }, }; assert_eq!("unexpected token: \"error\"", error.to_string()); From c560bccc26336882a8cd6fce3aee3d9701f1f081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 16 May 2024 15:20:51 +0200 Subject: [PATCH 02/24] exlude compiletest from no_std, fix no_std doctest --- Cargo.toml | 5 +++++ src/lib.rs | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 2ce5abc..64b253f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,12 @@ members = ["impl"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] +features = ["std"] [features] std = [] default = ["std"] + +[[test]] +name = "compiletest" +required-features = ["std"] diff --git a/src/lib.rs b/src/lib.rs index 6a281ef..c22d672 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,7 @@ //! # Example //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use std::io; //! use thiserror::Error; //! @@ -62,6 +63,7 @@ //! which may be arbitrary expressions. For example: //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use std::i32; //! # use thiserror::Error; //! # @@ -77,6 +79,7 @@ //! as `.0`. //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use thiserror::Error; //! # //! # fn first_char(s: &String) -> char { @@ -106,6 +109,7 @@ //! `From` impl if there is a field for it. //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # const IGNORE: &str = stringify! { //! #[derive(Error, Debug)] //! pub enum MyError { @@ -129,6 +133,7 @@ //! std::error::Error` will work as a source. //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use std::fmt::{self, Display}; //! # use thiserror::Error; //! # @@ -151,6 +156,7 @@ //! `std::backtrace::Backtrace`. //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # const IGNORE: &str = stringify! { //! use std::backtrace::Backtrace; //! @@ -168,6 +174,7 @@ //! both layers of the error share the same backtrace. //! //! ```rust +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # const IGNORE: &str = stringify! { //! #[derive(Error, Debug)] //! pub enum MyError { @@ -185,6 +192,7 @@ //! "anything else" variant. //! //! ``` +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use thiserror::Error; //! # //! #[derive(Error, Debug)] @@ -203,6 +211,7 @@ //! able to evolve without breaking the crate's public API. //! //! ``` +//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use thiserror::Error; //! # //! // PublicError is public, but opaque and easy to keep compatible. From ec51e633f7bf4ea4aae1e0de077ef9c266dc3335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 16 May 2024 15:27:35 +0200 Subject: [PATCH 03/24] undo Error->MyError renames --- src/lib.rs | 6 ++++-- tests/test_deprecated.rs | 2 +- tests/test_lints.rs | 1 + tests/test_transparent.rs | 11 ++++++----- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c22d672..e4c8a9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,7 @@ //! # use thiserror::Error; //! # //! #[derive(Error, Debug)] -//! pub enum MyError { +//! pub enum Error { //! #[error("invalid rdo_lookahead_frames {0} (expected < {})", i32::MAX)] //! InvalidLookahead(u32), //! } @@ -93,7 +93,7 @@ //! # } //! # //! #[derive(Error, Debug)] -//! pub enum MyError { +//! pub enum Error { //! #[error("first letter must be lowercase but was {:?}", first_char(.0))] //! WrongCase(String), //! #[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)] @@ -257,10 +257,12 @@ mod provide; #[cfg(feature = "std")] #[doc(hidden)] +// Hidden re-export for derive macro pub use std::error; #[cfg(not(feature = "std"))] #[doc(hidden)] +// Hidden re-export for derive macro pub use core::error; pub use thiserror_impl::*; diff --git a/tests/test_deprecated.rs b/tests/test_deprecated.rs index 5fc9210..b184394 100644 --- a/tests/test_deprecated.rs +++ b/tests/test_deprecated.rs @@ -4,7 +4,7 @@ use thiserror::Error; #[derive(Error, Debug)] -pub enum MyError { +pub enum Error { #[deprecated] #[error("...")] Deprecated, diff --git a/tests/test_lints.rs b/tests/test_lints.rs index ac0a3fb..5edff9d 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -3,6 +3,7 @@ use thiserror::Error; +// std or core pub use thiserror::error::Error; #[test] diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 89153dc..701be2c 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), feature(error_in_core))] use anyhow::anyhow; +// std or core use thiserror::error::Error; use thiserror::Error; @@ -34,17 +35,17 @@ fn test_transparent_struct() { #[test] fn test_transparent_enum() { #[derive(Error, Debug)] - enum MyError { + enum Error { #[error("this failed")] This, #[error(transparent)] Other(anyhow::Error), } - let error = MyError::This; + let error = Error::This; assert_eq!("this failed", error.to_string()); - let error = MyError::Other(anyhow!("inner").context("outer")); + let error = Error::Other(anyhow!("inner").context("outer")); assert_eq!("outer", error.to_string()); assert_eq!("inner", error.source().unwrap().to_string()); } @@ -64,7 +65,7 @@ fn test_anyhow() { fn test_non_static() { #[derive(Error, Debug)] #[error(transparent)] - struct MyError<'a> { + struct Error<'a> { inner: ErrorKind<'a>, } @@ -74,7 +75,7 @@ fn test_non_static() { Unexpected { token: &'a str }, } - let error = MyError { + let error = Error { inner: ErrorKind::Unexpected { token: "error" }, }; assert_eq!("unexpected token: \"error\"", error.to_string()); From f7f6c77f900e9f19f79f0b1ef28b978889ae4035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 16 May 2024 15:38:53 +0200 Subject: [PATCH 04/24] use __private namespace --- impl/src/expand.rs | 22 ++++++++++++---------- src/aserror.rs | 2 +- src/lib.rs | 14 ++++---------- src/provide.rs | 2 +- tests/test_lints.rs | 2 +- tests/test_transparent.rs | 2 +- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index ce6a0c3..7c1e351 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -36,7 +36,7 @@ fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream { #error #[allow(unused_qualifications)] - impl #impl_generics ::thiserror::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 @@ -60,17 +60,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!(::thiserror::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=> - ::thiserror::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!(::thiserror::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()?)) @@ -88,7 +90,7 @@ fn impl_struct(input: Struct) -> TokenStream { }; let source_method = source_body.map(|body| { quote! { - fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::error::Error + 'static)> { use thiserror::__private::AsDynError as _; #body } @@ -211,7 +213,7 @@ fn impl_struct(input: Struct) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics ::thiserror::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 } @@ -231,11 +233,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!(::thiserror::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=> - ::thiserror::error::Error::source(transparent.as_dyn_error()) + ::thiserror::__private::error::Error::source(transparent.as_dyn_error()) }; quote! { #ty::#ident {#member: transparent} => #source, @@ -244,7 +246,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!(::thiserror::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()?)) @@ -467,7 +469,7 @@ fn impl_enum(input: Enum) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics ::thiserror::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 } diff --git a/src/aserror.rs b/src/aserror.rs index 5c4abdb..a55da38 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,4 +1,4 @@ -use crate::error::Error; +use crate::__private::error::Error; use core::panic::UnwindSafe; #[doc(hidden)] diff --git a/src/lib.rs b/src/lib.rs index e4c8a9a..8aafb4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -255,16 +255,6 @@ mod display; #[cfg(error_generic_member_access)] mod provide; -#[cfg(feature = "std")] -#[doc(hidden)] -// Hidden re-export for derive macro -pub use std::error; - -#[cfg(not(feature = "std"))] -#[doc(hidden)] -// Hidden re-export for derive macro -pub use core::error; - pub use thiserror_impl::*; // Not public API. @@ -277,4 +267,8 @@ pub mod __private { #[cfg(error_generic_member_access)] #[doc(hidden)] pub use crate::provide::ThiserrorProvide; + #[cfg(not(feature = "std"))] + pub use core::error; + #[cfg(feature = "std")] + pub use std::error; } diff --git a/src/provide.rs b/src/provide.rs index f82a041..45151d3 100644 --- a/src/provide.rs +++ b/src/provide.rs @@ -1,4 +1,4 @@ -use crate::error::{Error, Request}; +use crate::__private::error::{Error, Request}; #[doc(hidden)] pub trait ThiserrorProvide: Sealed { diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 5edff9d..5196285 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -4,7 +4,7 @@ use thiserror::Error; // std or core -pub use thiserror::error::Error; +pub use thiserror::__private::error::Error; #[test] fn test_unused_qualifications() { diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 701be2c..f527c1a 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -2,8 +2,8 @@ use anyhow::anyhow; // std or core -use thiserror::error::Error; use thiserror::Error; +use thiserror::__private::error::Error; #[cfg(feature = "std")] #[test] From f1d9df3046301b29687a523a4ec579ef69dc79e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 16 May 2024 15:45:01 +0200 Subject: [PATCH 05/24] add comment --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 64b253f..a4b7ee2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,5 @@ default = ["std"] [[test]] name = "compiletest" +# Merely to avoid spraying cfg(error_in_core) required-features = ["std"] From 3663fd16b7ae40c966e1b7e8926a2ca4164730be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 22 May 2024 10:06:53 +0200 Subject: [PATCH 06/24] use explicit doc(test(attr)) --- src/lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 95f8ca8..0313847 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ //! # Example //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use std::io; //! use thiserror::Error; //! @@ -63,7 +62,6 @@ //! which may be arbitrary expressions. For example: //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use core::i32; //! # use thiserror::Error; //! # @@ -79,7 +77,6 @@ //! as `.0`. //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use thiserror::Error; //! # //! # fn first_char(s: &String) -> char { @@ -109,7 +106,6 @@ //! `From` impl if there is a field for it. //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # const IGNORE: &str = stringify! { //! #[derive(Error, Debug)] //! pub enum MyError { @@ -133,7 +129,6 @@ //! std::error::Error` will work as a source. //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use core::fmt::{self, Display}; //! # use thiserror::Error; //! # @@ -156,7 +151,6 @@ //! `std::backtrace::Backtrace`. //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # const IGNORE: &str = stringify! { //! use std::backtrace::Backtrace; //! @@ -174,7 +168,6 @@ //! both layers of the error share the same backtrace. //! //! ```rust -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # const IGNORE: &str = stringify! { //! #[derive(Error, Debug)] //! pub enum MyError { @@ -192,7 +185,6 @@ //! "anything else" variant. //! //! ``` -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use thiserror::Error; //! # //! #[derive(Error, Debug)] @@ -211,7 +203,6 @@ //! able to evolve without breaking the crate's public API. //! //! ``` -//! # #![cfg_attr(not(feature = "std"), feature(error_in_core))] //! # use thiserror::Error; //! # //! // PublicError is public, but opaque and easy to keep compatible. @@ -245,7 +236,12 @@ clippy::wildcard_imports )] #![cfg_attr(error_generic_member_access, feature(error_generic_member_access))] -#![cfg_attr(not(feature = "std"), no_std, feature(error_in_core))] +#![cfg_attr( + not(feature = "std"), + no_std, + feature(error_in_core), + doc(test(attr(feature(error_in_core)))) +)] #[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))] compile_error!("Build script probe failed to compile."); @@ -268,7 +264,9 @@ pub mod __private { #[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; } From 61b1c171a6fd53f594e98a40674252573580bff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 14 Jun 2024 13:14:57 +0200 Subject: [PATCH 07/24] error_in_core is stable --- Cargo.toml | 5 ----- impl/src/expand.rs | 28 +++++++++++++--------------- src/aserror.rs | 2 +- src/lib.rs | 13 +------------ src/provide.rs | 2 +- tests/test_backtrace.rs | 1 - tests/test_deprecated.rs | 1 - tests/test_display.rs | 1 - tests/test_error.rs | 1 - tests/test_expr.rs | 1 - tests/test_from.rs | 1 - tests/test_generics.rs | 1 - tests/test_lints.rs | 4 +--- tests/test_source.rs | 2 -- tests/test_transparent.rs | 5 +---- 15 files changed, 18 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 235fcb5..e1802ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,3 @@ features = ["std"] [features] std = [] default = ["std"] - -[[test]] -name = "compiletest" -# Merely to avoid spraying cfg(error_in_core) -required-features = ["std"] diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 7c1e351..8e7b315 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -36,7 +36,7 @@ fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream { #error #[allow(unused_qualifications)] - impl #impl_generics ::thiserror::__private::error::Error for #ty #ty_generics #where_clause + impl #impl_generics ::core::error::Error for #ty #ty_generics #where_clause where // Work around trivial bounds being unstable. // https://github.com/rust-lang/rust/issues/48214 @@ -60,19 +60,17 @@ 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!(::thiserror::__private::error::Error)); + error_inferred_bounds.insert(only_field.ty, quote!(::core::error::Error)); } let member = &only_field.member; Some(quote_spanned! {transparent_attr.span=> - ::thiserror::__private::error::Error::source(self.#member.as_dyn_error()) + ::core::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!(::thiserror::__private::error::Error + 'static)); + error_inferred_bounds.insert(ty, quote!(::core::error::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.member_span()=> .as_ref()?)) @@ -90,7 +88,7 @@ fn impl_struct(input: Struct) -> TokenStream { }; let source_method = source_body.map(|body| { quote! { - fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> { use thiserror::__private::AsDynError as _; #body } @@ -143,7 +141,7 @@ fn impl_struct(input: Struct) -> TokenStream { } }; quote! { - fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) { + fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) { #body } } @@ -213,7 +211,7 @@ fn impl_struct(input: Struct) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics ::thiserror::__private::error::Error for #ty #ty_generics #error_where_clause { + impl #impl_generics ::core::error::Error for #ty #ty_generics #error_where_clause { #source_method #provide_method } @@ -233,11 +231,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!(::thiserror::__private::error::Error)); + error_inferred_bounds.insert(only_field.ty, quote!(::core::error::Error)); } let member = &only_field.member; let source = quote_spanned! {transparent_attr.span=> - ::thiserror::__private::error::Error::source(transparent.as_dyn_error()) + ::core::error::Error::source(transparent.as_dyn_error()) }; quote! { #ty::#ident {#member: transparent} => #source, @@ -246,7 +244,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!(::thiserror::__private::error::Error + 'static)); + error_inferred_bounds.insert(ty, quote!(::core::error::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.member_span()=> .as_ref()?)) @@ -267,7 +265,7 @@ fn impl_enum(input: Enum) -> TokenStream { } }); Some(quote! { - fn source(&self) -> ::core::option::Option<&(dyn std::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> { use thiserror::__private::AsDynError as _; #[allow(deprecated)] match self { @@ -372,7 +370,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 ::core::error::Request<'_request>) { #[allow(deprecated)] match self { #(#arms)* @@ -469,7 +467,7 @@ fn impl_enum(input: Enum) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics ::thiserror::__private::error::Error for #ty #ty_generics #error_where_clause { + impl #impl_generics ::core::error::Error for #ty #ty_generics #error_where_clause { #source_method #provide_method } diff --git a/src/aserror.rs b/src/aserror.rs index a55da38..aa1e3fa 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,4 +1,4 @@ -use crate::__private::error::Error; +use core::error::Error; use core::panic::UnwindSafe; #[doc(hidden)] diff --git a/src/lib.rs b/src/lib.rs index 0313847..0281acf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -236,12 +236,7 @@ clippy::wildcard_imports )] #![cfg_attr(error_generic_member_access, feature(error_generic_member_access))] -#![cfg_attr( - not(feature = "std"), - no_std, - feature(error_in_core), - doc(test(attr(feature(error_in_core)))) -)] +#![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."); @@ -263,10 +258,4 @@ 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 45151d3..4b2f06a 100644 --- a/src/provide.rs +++ b/src/provide.rs @@ -1,4 +1,4 @@ -use crate::__private::error::{Error, Request}; +use core::error::{Error, Request}; #[doc(hidden)] pub trait ThiserrorProvide: Sealed { diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index d2d0e27..4710d45 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))] use thiserror::Error; diff --git a/tests/test_deprecated.rs b/tests/test_deprecated.rs index b184394..5524666 100644 --- a/tests/test_deprecated.rs +++ b/tests/test_deprecated.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![deny(deprecated, clippy::all, clippy::pedantic)] use thiserror::Error; diff --git a/tests/test_display.rs b/tests/test_display.rs index d30f1cb..c05d157 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_raw_string_hashes, clippy::uninlined_format_args)] use core::fmt::{self, Display}; diff --git a/tests/test_error.rs b/tests/test_error.rs index f0e9112..eb52cef 100644 --- a/tests/test_error.rs +++ b/tests/test_error.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(dead_code)] use core::fmt::{self, Display}; diff --git a/tests/test_expr.rs b/tests/test_expr.rs index cb38f87..c5e3b4b 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::iter_cloned_collect, clippy::uninlined_format_args)] use core::fmt::Display; diff --git a/tests/test_from.rs b/tests/test_from.rs index f1b34a4..51af40b 100644 --- a/tests/test_from.rs +++ b/tests/test_from.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::extra_unused_type_parameters)] use std::io; diff --git a/tests/test_generics.rs b/tests/test_generics.rs index 3c871e6..ac5d6f3 100644 --- a/tests/test_generics.rs +++ b/tests/test_generics.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_late_init, clippy::uninlined_format_args)] use core::fmt::{self, Debug, Display}; diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 5196285..73d6b83 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -1,10 +1,8 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::mixed_attributes_style)] use thiserror::Error; -// std or core -pub use thiserror::__private::error::Error; +pub use core::error::Error; #[test] fn test_unused_qualifications() { diff --git a/tests/test_source.rs b/tests/test_source.rs index a93fec3..637f4ac 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,5 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] - use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 168e5a3..201b3df 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,9 +1,6 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] - use anyhow::anyhow; +use core::error::Error; use thiserror::Error; -// std or core -use thiserror::__private::error::Error; #[cfg(feature = "std")] #[test] From 3e792e0f4f0c18767f9ea6a36acf5260a339d0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 14 Jun 2024 13:15:29 +0200 Subject: [PATCH 08/24] {std -> core}:error: update docs/comment --- README.md | 10 +++++----- build/probe.rs | 2 +- impl/src/valid.rs | 2 +- src/lib.rs | 10 +++++----- tests/test_backtrace.rs | 4 ++-- tests/test_lints.rs | 2 +- tests/test_source.rs | 2 +- tests/ui/lifetime.stderr | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 9de063c..97e0881 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ derive(Error) [build status](https://github.com/dtolnay/thiserror/actions?query=branch%3Amaster) This library provides a convenient derive macro for the standard library's -[`std::error::Error`] trait. +[`core::error::Error`] trait. -[`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html +[`core::error::Error`]: https://doc.rust-lang.org/core/error/trait.Error.html ```toml [dependencies] @@ -46,7 +46,7 @@ pub enum DataStoreError { ## Details - Thiserror deliberately does not appear in your public API. You get the same - thing as if you had written an implementation of `std::error::Error` by hand, + thing as if you had written an implementation of `core::error::Error` by hand, and switching from handwritten impls to thiserror or vice versa is not a breaking change. @@ -112,8 +112,8 @@ pub enum DataStoreError { The `#[from]` attribute always implies that the same field is `#[source]`, so you don't ever need to specify both attributes. - Any error type that implements `std::error::Error` or dereferences to `dyn - std::error::Error` will work as a source. + Any error type that implements `core::error::Error` or dereferences to `dyn + core::error::Error` will work as a source. ```rust #[derive(Error, Debug)] diff --git a/build/probe.rs b/build/probe.rs index faf25c5..25f336b 100644 --- a/build/probe.rs +++ b/build/probe.rs @@ -5,7 +5,7 @@ #![feature(error_generic_member_access)] use core::fmt::{self, Debug, Display}; -use std::error::{Error, Request}; +use core::error::{Error, Request}; struct MyError(Thing); struct Thing; diff --git a/impl/src/valid.rs b/impl/src/valid.rs index cf5b859..686297d 100644 --- a/impl/src/valid.rs +++ b/impl/src/valid.rs @@ -195,7 +195,7 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> { if contains_non_static_lifetime(source_field.ty) { return Err(Error::new_spanned( &source_field.original.ty, - "non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static", + "non-static lifetimes are not allowed in the source of an error, because core::error::Error requires the source is dyn Error + 'static", )); } } diff --git a/src/lib.rs b/src/lib.rs index 0281acf..5aefa2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,9 +7,9 @@ //!
//! //! This library provides a convenient derive macro for the standard library's -//! [`std::error::Error`] trait. +//! [`core::error::Error`] trait. //! -//! [`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html +//! [`core::error::Error`]: https://doc.rust-lang.org/core/error/trait.Error.html //! //!
//! @@ -40,7 +40,7 @@ //! # Details //! //! - Thiserror deliberately does not appear in your public API. You get the -//! same thing as if you had written an implementation of `std::error::Error` +//! same thing as if you had written an implementation of `core::error::Error` //! by hand, and switching from handwritten impls to thiserror or vice versa //! is not a breaking change. //! @@ -125,8 +125,8 @@ //! The `#[from]` attribute always implies that the same field is `#[source]`, //! so you don't ever need to specify both attributes. //! -//! Any error type that implements `std::error::Error` or dereferences to `dyn -//! std::error::Error` will work as a source. +//! Any error type that implements `core::error::Error` or dereferences to `dyn +//! core::error::Error` will work as a source. //! //! ```rust //! # use core::fmt::{self, Display}; diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 4710d45..85ed70e 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -16,8 +16,8 @@ pub struct InnerBacktrace { #[cfg(thiserror_nightly_testing)] pub mod structs { use super::{Inner, InnerBacktrace}; + use core::error::{self, Error}; use std::backtrace::Backtrace; - use std::error::{self, Error}; use std::sync::Arc; use thiserror::Error; @@ -150,8 +150,8 @@ pub mod structs { #[cfg(thiserror_nightly_testing)] pub mod enums { use super::{Inner, InnerBacktrace}; + use core::error; use std::backtrace::Backtrace; - use std::error; use std::sync::Arc; use thiserror::Error; diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 73d6b83..75e8919 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -9,7 +9,7 @@ fn test_unused_qualifications() { #![deny(unused_qualifications)] // Expansion of derive(Error) macro can't know whether something like - // std::error::Error is already imported in the caller's scope so it must + // core::error::Error is already imported in the caller's scope so it must // suppress unused_qualifications. #[derive(Debug, Error)] diff --git a/tests/test_source.rs b/tests/test_source.rs index 637f4ac..08b840b 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,4 +1,4 @@ -use std::error::Error as StdError; +use core::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/ui/lifetime.stderr b/tests/ui/lifetime.stderr index 8b58136..f9957ca 100644 --- a/tests/ui/lifetime.stderr +++ b/tests/ui/lifetime.stderr @@ -1,10 +1,10 @@ -error: non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static +error: non-static lifetimes are not allowed in the source of an error, because core::error::Error requires the source is dyn Error + 'static --> tests/ui/lifetime.rs:6:26 | 6 | struct Error<'a>(#[from] Inner<'a>); | ^^^^^^^^^ -error: non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static +error: non-static lifetimes are not allowed in the source of an error, because core::error::Error requires the source is dyn Error + 'static --> tests/ui/lifetime.rs:15:17 | 15 | Foo(#[from] Generic<&'a str>), From b2782582bb7ded192b6f8ed52d0570bbe9c09cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 14 Jun 2024 13:30:38 +0200 Subject: [PATCH 09/24] docs.rs: std is default --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e1802ef..3b90e13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ members = ["impl"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] -features = ["std"] [features] std = [] From 8cbdc4ef44343085020aea61adce0835020001d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 14 Jun 2024 15:13:15 +0200 Subject: [PATCH 10/24] bump MSRV to 1.81 --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 313d892..f1bd5ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.56.0] + rust: [nightly, beta, stable, 1.81.0] timeout-minutes: 45 steps: - uses: actions/checkout@v4 diff --git a/Cargo.toml b/Cargo.toml index 3b90e13..9bd290d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" keywords = ["error", "error-handling", "derive"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/thiserror" -rust-version = "1.56" +rust-version = "1.81" [dependencies] thiserror-impl = { version = "=1.0.61", path = "impl" } diff --git a/README.md b/README.md index 97e0881..5f6af57 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This library provides a convenient derive macro for the standard library's thiserror = "1.0" ``` -*Compiler support: requires rustc 1.56+* +*Compiler support: requires rustc 1.81+*
From 7fa183be01bdd8510a1c7d4b7999aa0de86472db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 15:11:41 +0200 Subject: [PATCH 11/24] Revert "bump MSRV to 1.81" This reverts commit 8cbdc4ef44343085020aea61adce0835020001d8. --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6a54d3..65a20f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.81.0] + rust: [nightly, beta, stable, 1.56.0] timeout-minutes: 45 steps: - uses: actions/checkout@v4 diff --git a/Cargo.toml b/Cargo.toml index 03eba1c..a4cbab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" keywords = ["error", "error-handling", "derive"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/thiserror" -rust-version = "1.81" +rust-version = "1.56" [dependencies] thiserror-impl = { version = "=1.0.63", path = "impl" } diff --git a/README.md b/README.md index 9c6a80e..6c6c576 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This library provides a convenient derive macro for the standard library's thiserror = "1.0" ``` -*Compiler support: requires rustc 1.81+* +*Compiler support: requires rustc 1.56+*
From 28ce40b32b556946ea86f7c6dc876ebd022b63de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 15:11:44 +0200 Subject: [PATCH 12/24] Revert "{std -> core}:error: update docs/comment" This reverts commit 3e792e0f4f0c18767f9ea6a36acf5260a339d0cb. --- README.md | 10 +++++----- build/probe.rs | 2 +- impl/src/valid.rs | 2 +- src/lib.rs | 10 +++++----- tests/test_backtrace.rs | 4 ++-- tests/test_lints.rs | 2 +- tests/test_source.rs | 2 +- tests/ui/lifetime.stderr | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 6c6c576..3b7d743 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ derive(Error) [build status](https://github.com/dtolnay/thiserror/actions?query=branch%3Amaster) This library provides a convenient derive macro for the standard library's -[`core::error::Error`] trait. +[`std::error::Error`] trait. -[`core::error::Error`]: https://doc.rust-lang.org/core/error/trait.Error.html +[`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html ```toml [dependencies] @@ -46,7 +46,7 @@ pub enum DataStoreError { ## Details - Thiserror deliberately does not appear in your public API. You get the same - thing as if you had written an implementation of `core::error::Error` by hand, + thing as if you had written an implementation of `std::error::Error` by hand, and switching from handwritten impls to thiserror or vice versa is not a breaking change. @@ -110,8 +110,8 @@ pub enum DataStoreError { The `#[from]` attribute always implies that the same field is `#[source]`, so you don't ever need to specify both attributes. - Any error type that implements `core::error::Error` or dereferences to `dyn - core::error::Error` will work as a source. + Any error type that implements `std::error::Error` or dereferences to `dyn + std::error::Error` will work as a source. ```rust #[derive(Error, Debug)] diff --git a/build/probe.rs b/build/probe.rs index 25f336b..faf25c5 100644 --- a/build/probe.rs +++ b/build/probe.rs @@ -5,7 +5,7 @@ #![feature(error_generic_member_access)] use core::fmt::{self, Debug, Display}; -use core::error::{Error, Request}; +use std::error::{Error, Request}; struct MyError(Thing); struct Thing; diff --git a/impl/src/valid.rs b/impl/src/valid.rs index 686297d..cf5b859 100644 --- a/impl/src/valid.rs +++ b/impl/src/valid.rs @@ -195,7 +195,7 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> { if contains_non_static_lifetime(source_field.ty) { return Err(Error::new_spanned( &source_field.original.ty, - "non-static lifetimes are not allowed in the source of an error, because core::error::Error requires the source is dyn Error + 'static", + "non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static", )); } } diff --git a/src/lib.rs b/src/lib.rs index 49f1add..a93e00b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,9 +7,9 @@ //!
//! //! This library provides a convenient derive macro for the standard library's -//! [`core::error::Error`] trait. +//! [`std::error::Error`] trait. //! -//! [`core::error::Error`]: https://doc.rust-lang.org/core/error/trait.Error.html +//! [`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html //! //!
//! @@ -40,7 +40,7 @@ //! # Details //! //! - Thiserror deliberately does not appear in your public API. You get the -//! same thing as if you had written an implementation of `core::error::Error` +//! same thing as if you had written an implementation of `std::error::Error` //! by hand, and switching from handwritten impls to thiserror or vice versa //! is not a breaking change. //! @@ -137,8 +137,8 @@ //! The `#[from]` attribute always implies that the same field is `#[source]`, //! so you don't ever need to specify both attributes. //! -//! Any error type that implements `core::error::Error` or dereferences to `dyn -//! core::error::Error` will work as a source. +//! Any error type that implements `std::error::Error` or dereferences to `dyn +//! std::error::Error` will work as a source. //! //! ```rust //! # use core::fmt::{self, Display}; diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index b72c72c..8f11da3 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -16,8 +16,8 @@ pub struct InnerBacktrace { #[cfg(thiserror_nightly_testing)] pub mod structs { use super::{Inner, InnerBacktrace}; - use core::error::{self, Error}; use std::backtrace::Backtrace; + use std::error::{self, Error}; use std::sync::Arc; use thiserror::Error; @@ -150,8 +150,8 @@ pub mod structs { #[cfg(thiserror_nightly_testing)] pub mod enums { use super::{Inner, InnerBacktrace}; - use core::error; use std::backtrace::Backtrace; + use std::error; use std::sync::Arc; use thiserror::Error; diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 75e8919..73d6b83 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -9,7 +9,7 @@ fn test_unused_qualifications() { #![deny(unused_qualifications)] // Expansion of derive(Error) macro can't know whether something like - // core::error::Error is already imported in the caller's scope so it must + // std::error::Error is already imported in the caller's scope so it must // suppress unused_qualifications. #[derive(Debug, Error)] diff --git a/tests/test_source.rs b/tests/test_source.rs index 08b840b..637f4ac 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,4 +1,4 @@ -use core::error::Error as StdError; +use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/ui/lifetime.stderr b/tests/ui/lifetime.stderr index f9957ca..8b58136 100644 --- a/tests/ui/lifetime.stderr +++ b/tests/ui/lifetime.stderr @@ -1,10 +1,10 @@ -error: non-static lifetimes are not allowed in the source of an error, because core::error::Error requires the source is dyn Error + 'static +error: non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static --> tests/ui/lifetime.rs:6:26 | 6 | struct Error<'a>(#[from] Inner<'a>); | ^^^^^^^^^ -error: non-static lifetimes are not allowed in the source of an error, because core::error::Error requires the source is dyn Error + 'static +error: non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static --> tests/ui/lifetime.rs:15:17 | 15 | Foo(#[from] Generic<&'a str>), From 0b058bcb5e54646b38ce1db7d8c1f0c09eec2118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 15:11:48 +0200 Subject: [PATCH 13/24] Revert "error_in_core is stable" This reverts commit 61b1c171a6fd53f594e98a40674252573580bff3. --- Cargo.toml | 5 +++++ impl/src/expand.rs | 28 +++++++++++++++------------- src/aserror.rs | 2 +- src/lib.rs | 13 ++++++++++++- src/provide.rs | 2 +- tests/test_backtrace.rs | 1 + tests/test_deprecated.rs | 1 + tests/test_display.rs | 1 + tests/test_error.rs | 1 + tests/test_expr.rs | 1 + tests/test_from.rs | 1 + tests/test_generics.rs | 1 + tests/test_lints.rs | 4 +++- tests/test_source.rs | 2 ++ tests/test_transparent.rs | 5 ++++- 15 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a4cbab4..24c9f6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,8 @@ rustdoc-args = ["--generate-link-to-definition"] [features] std = [] default = ["std"] + +[[test]] +name = "compiletest" +# Merely to avoid spraying cfg(error_in_core) +required-features = ["std"] diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 8e7b315..7c1e351 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -36,7 +36,7 @@ fn fallback(input: &DeriveInput, error: syn::Error) -> TokenStream { #error #[allow(unused_qualifications)] - impl #impl_generics ::core::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 @@ -60,17 +60,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!(::core::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=> - ::core::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!(::core::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()?)) @@ -88,7 +90,7 @@ fn impl_struct(input: Struct) -> TokenStream { }; let source_method = source_body.map(|body| { quote! { - fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::error::Error + 'static)> { use thiserror::__private::AsDynError as _; #body } @@ -141,7 +143,7 @@ fn impl_struct(input: Struct) -> TokenStream { } }; quote! { - fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) { + fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) { #body } } @@ -211,7 +213,7 @@ fn impl_struct(input: Struct) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics ::core::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 } @@ -231,11 +233,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!(::core::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=> - ::core::error::Error::source(transparent.as_dyn_error()) + ::thiserror::__private::error::Error::source(transparent.as_dyn_error()) }; quote! { #ty::#ident {#member: transparent} => #source, @@ -244,7 +246,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!(::core::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()?)) @@ -265,7 +267,7 @@ fn impl_enum(input: Enum) -> TokenStream { } }); Some(quote! { - fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn std::error::Error + 'static)> { use thiserror::__private::AsDynError as _; #[allow(deprecated)] match self { @@ -370,7 +372,7 @@ fn impl_enum(input: Enum) -> TokenStream { } }); Some(quote! { - fn provide<'_request>(&'_request self, #request: &mut ::core::error::Request<'_request>) { + fn provide<'_request>(&'_request self, #request: &mut std::error::Request<'_request>) { #[allow(deprecated)] match self { #(#arms)* @@ -467,7 +469,7 @@ fn impl_enum(input: Enum) -> TokenStream { quote! { #[allow(unused_qualifications)] - impl #impl_generics ::core::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 } diff --git a/src/aserror.rs b/src/aserror.rs index aa1e3fa..a55da38 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,4 +1,4 @@ -use core::error::Error; +use crate::__private::error::Error; use core::panic::UnwindSafe; #[doc(hidden)] diff --git a/src/lib.rs b/src/lib.rs index a93e00b..5f6e34e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,7 +266,12 @@ clippy::wildcard_imports )] #![cfg_attr(error_generic_member_access, feature(error_generic_member_access))] -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr( + not(feature = "std"), + no_std, + feature(error_in_core), + doc(test(attr(feature(error_in_core)))) +)] #[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))] compile_error!("Build script probe failed to compile."); @@ -288,4 +293,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 4b2f06a..45151d3 100644 --- a/src/provide.rs +++ b/src/provide.rs @@ -1,4 +1,4 @@ -use core::error::{Error, Request}; +use crate::__private::error::{Error, Request}; #[doc(hidden)] pub trait ThiserrorProvide: Sealed { diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 8f11da3..46e1ce1 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))] use thiserror::Error; diff --git a/tests/test_deprecated.rs b/tests/test_deprecated.rs index 5524666..b184394 100644 --- a/tests/test_deprecated.rs +++ b/tests/test_deprecated.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![deny(deprecated, clippy::all, clippy::pedantic)] use thiserror::Error; diff --git a/tests/test_display.rs b/tests/test_display.rs index 7a9057c..f589d52 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_raw_string_hashes, clippy::uninlined_format_args)] use core::fmt::{self, Display}; diff --git a/tests/test_error.rs b/tests/test_error.rs index eb52cef..f0e9112 100644 --- a/tests/test_error.rs +++ b/tests/test_error.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(dead_code)] use core::fmt::{self, Display}; diff --git a/tests/test_expr.rs b/tests/test_expr.rs index c5e3b4b..cb38f87 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::iter_cloned_collect, clippy::uninlined_format_args)] use core::fmt::Display; diff --git a/tests/test_from.rs b/tests/test_from.rs index 51af40b..f1b34a4 100644 --- a/tests/test_from.rs +++ b/tests/test_from.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::extra_unused_type_parameters)] use std::io; diff --git a/tests/test_generics.rs b/tests/test_generics.rs index d7790e2..65ca3f1 100644 --- a/tests/test_generics.rs +++ b/tests/test_generics.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_late_init, clippy::uninlined_format_args)] use core::fmt::{self, Debug, Display}; diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 73d6b83..5196285 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -1,8 +1,10 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::mixed_attributes_style)] use thiserror::Error; -pub use core::error::Error; +// std or core +pub use thiserror::__private::error::Error; #[test] fn test_unused_qualifications() { diff --git a/tests/test_source.rs b/tests/test_source.rs index 637f4ac..a93fec3 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] + use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 201b3df..168e5a3 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,6 +1,9 @@ +#![cfg_attr(not(feature = "std"), feature(error_in_core))] + use anyhow::anyhow; -use core::error::Error; use thiserror::Error; +// std or core +use thiserror::__private::error::Error; #[cfg(feature = "std")] #[test] From e7e9341e2007d2188e19055ae633aa21a0df0028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 15:32:45 +0200 Subject: [PATCH 14/24] minimalize changes, maintain MSRV for `std` * let integration tests require `std` where necessary * don't use error_in_core anymore --- Cargo.toml | 5 ----- src/lib.rs | 7 +------ tests/compiletest.rs | 1 + tests/test_backtrace.rs | 1 - tests/test_deprecated.rs | 1 - tests/test_display.rs | 1 - tests/test_error.rs | 1 - tests/test_expr.rs | 1 - tests/test_from.rs | 1 - tests/test_generics.rs | 1 - tests/test_lints.rs | 5 ++--- tests/test_source.rs | 2 -- tests/test_transparent.rs | 9 +++------ 13 files changed, 7 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 24c9f6c..a4cbab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,3 @@ rustdoc-args = ["--generate-link-to-definition"] [features] std = [] default = ["std"] - -[[test]] -name = "compiletest" -# Merely to avoid spraying cfg(error_in_core) -required-features = ["std"] diff --git a/src/lib.rs b/src/lib.rs index 5f6e34e..46b45d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,12 +266,7 @@ clippy::wildcard_imports )] #![cfg_attr(error_generic_member_access, feature(error_generic_member_access))] -#![cfg_attr( - not(feature = "std"), - no_std, - feature(error_in_core), - doc(test(attr(feature(error_in_core)))) -)] +#![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."); diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 23a6a06..28b9055 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,4 +1,5 @@ #[rustversion::attr(not(nightly), ignore = "requires nightly")] +#[cfg_attr(not(feature = "std"), ignore = "requires std")] #[cfg_attr(miri, ignore = "incompatible with miri")] #[test] fn ui() { diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 46e1ce1..8f11da3 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))] use thiserror::Error; diff --git a/tests/test_deprecated.rs b/tests/test_deprecated.rs index b184394..5524666 100644 --- a/tests/test_deprecated.rs +++ b/tests/test_deprecated.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![deny(deprecated, clippy::all, clippy::pedantic)] use thiserror::Error; diff --git a/tests/test_display.rs b/tests/test_display.rs index f589d52..7a9057c 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_raw_string_hashes, clippy::uninlined_format_args)] use core::fmt::{self, Display}; diff --git a/tests/test_error.rs b/tests/test_error.rs index f0e9112..eb52cef 100644 --- a/tests/test_error.rs +++ b/tests/test_error.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(dead_code)] use core::fmt::{self, Display}; diff --git a/tests/test_expr.rs b/tests/test_expr.rs index cb38f87..c5e3b4b 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::iter_cloned_collect, clippy::uninlined_format_args)] use core::fmt::Display; diff --git a/tests/test_from.rs b/tests/test_from.rs index f1b34a4..51af40b 100644 --- a/tests/test_from.rs +++ b/tests/test_from.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::extra_unused_type_parameters)] use std::io; diff --git a/tests/test_generics.rs b/tests/test_generics.rs index 65ca3f1..d7790e2 100644 --- a/tests/test_generics.rs +++ b/tests/test_generics.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] #![allow(clippy::needless_late_init, clippy::uninlined_format_args)] use core::fmt::{self, Debug, Display}; diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 5196285..88fcabc 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -1,10 +1,9 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] +#![cfg(feature = "std")] #![allow(clippy::mixed_attributes_style)] use thiserror::Error; -// std or core -pub use thiserror::__private::error::Error; +pub use std::error::Error; #[test] fn test_unused_qualifications() { diff --git a/tests/test_source.rs b/tests/test_source.rs index a93fec3..637f4ac 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,5 +1,3 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] - use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 168e5a3..5af10db 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,15 +1,12 @@ -#![cfg_attr(not(feature = "std"), feature(error_in_core))] +#![cfg(feature = "std")] use anyhow::anyhow; +use std::error::Error as _; +use std::io; use thiserror::Error; -// std or core -use thiserror::__private::error::Error; -#[cfg(feature = "std")] #[test] fn test_transparent_struct() { - use std::io; - #[derive(Error, Debug)] #[error(transparent)] struct Error(ErrorKind); From bfd73573b298b1b2e32fb9766fb12e98c859f657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 15:42:02 +0200 Subject: [PATCH 15/24] test 1.81 and no_std --- .github/workflows/ci.yml | 5 ++++- Cargo.toml | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65a20f5..1b80db6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.56.0] + rust: [nightly, beta, stable, 1.56.0, 1.81.0] timeout-minutes: 45 steps: - uses: actions/checkout@v4 @@ -39,6 +39,9 @@ jobs: run: echo RUSTFLAGS=${RUSTFLAGS}\ --cfg=thiserror_nightly_testing >> $GITHUB_ENV if: matrix.rust == 'nightly' - run: cargo test --all + - name: Enable no_std + run: cargo check --no-default-features + if: matrix.rust != '1.56.0' - uses: actions/upload-artifact@v4 if: matrix.rust == 'nightly' && always() with: diff --git a/Cargo.toml b/Cargo.toml index a4cbab4..eda6152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ 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.63", path = "impl" } From 706fb6aa5f1f5e40db3294263ed62ddb76af22a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 15:46:31 +0200 Subject: [PATCH 16/24] ci no_std: not check but test, 1.81 not necessary --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b80db6..dbac460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.56.0, 1.81.0] + rust: [nightly, beta, stable, 1.56.0] timeout-minutes: 45 steps: - uses: actions/checkout@v4 @@ -40,7 +40,7 @@ jobs: if: matrix.rust == 'nightly' - run: cargo test --all - name: Enable no_std - run: cargo check --no-default-features + run: cargo test --all --no-default-features if: matrix.rust != '1.56.0' - uses: actions/upload-artifact@v4 if: matrix.rust == 'nightly' && always() From a3d073b13e68db9155ddc6a87b733870fc8dc8f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 6 Sep 2024 16:01:47 +0200 Subject: [PATCH 17/24] compiletest: allow unused_attributes don't warn if more than one of the `ignore` conditions hold --- tests/compiletest.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 28b9055..b10fe63 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,3 +1,4 @@ +#[allow(unused_attributes)] #[rustversion::attr(not(nightly), ignore = "requires nightly")] #[cfg_attr(not(feature = "std"), ignore = "requires std")] #[cfg_attr(miri, ignore = "incompatible with miri")] From 89008c0105da03c179ae0b48cc1a63655a42f15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 9 Sep 2024 10:37:32 +0200 Subject: [PATCH 18/24] prefer required-features --- .github/workflows/ci.yml | 4 ++-- Cargo.toml | 19 +++++++++++++++++-- tests/compiletest.rs | 2 -- tests/test_lints.rs | 1 - tests/test_path.rs | 2 -- tests/test_transparent.rs | 2 -- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbac460..69e489a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,10 +38,10 @@ jobs: - name: Enable nightly-only tests run: echo RUSTFLAGS=${RUSTFLAGS}\ --cfg=thiserror_nightly_testing >> $GITHUB_ENV if: matrix.rust == 'nightly' - - run: cargo test --all - - name: Enable no_std + - 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() with: diff --git a/Cargo.toml b/Cargo.toml index eda6152..f1904b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,7 @@ 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" +# without the `std` feature (i.e. `no_std`): rust-version = "1.81" [dependencies] thiserror-impl = { version = "=1.0.63", path = "impl" } @@ -33,3 +32,19 @@ rustdoc-args = ["--generate-link-to-definition"] [features] std = [] default = ["std"] + +[[test]] +name = "compiletest" +required-features = ["std"] + +[[test]] +name = "test_lints" +required-features = ["std"] + +[[test]] +name = "test_path" +required-features = ["std"] + +[[test]] +name = "test_transparent" +required-features = ["std"] diff --git a/tests/compiletest.rs b/tests/compiletest.rs index b10fe63..23a6a06 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,6 +1,4 @@ -#[allow(unused_attributes)] #[rustversion::attr(not(nightly), ignore = "requires nightly")] -#[cfg_attr(not(feature = "std"), ignore = "requires std")] #[cfg_attr(miri, ignore = "incompatible with miri")] #[test] fn ui() { diff --git a/tests/test_lints.rs b/tests/test_lints.rs index 88fcabc..cafcbc0 100644 --- a/tests/test_lints.rs +++ b/tests/test_lints.rs @@ -1,4 +1,3 @@ -#![cfg(feature = "std")] #![allow(clippy::mixed_attributes_style)] use thiserror::Error; diff --git a/tests/test_path.rs b/tests/test_path.rs index 4912e53..f054077 100644 --- a/tests/test_path.rs +++ b/tests/test_path.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "std")] - use core::fmt::Display; use ref_cast::RefCast; use std::path::{Path, PathBuf}; diff --git a/tests/test_transparent.rs b/tests/test_transparent.rs index 5af10db..6f3c03e 100644 --- a/tests/test_transparent.rs +++ b/tests/test_transparent.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "std")] - use anyhow::anyhow; use std::error::Error as _; use std::io; From c9cd3b94146ae88d9b0558a74aa26808b9493c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 9 Sep 2024 10:47:48 +0200 Subject: [PATCH 19/24] compiletest doesn't need std --- Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1904b2..edf8158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,10 +33,6 @@ rustdoc-args = ["--generate-link-to-definition"] std = [] default = ["std"] -[[test]] -name = "compiletest" -required-features = ["std"] - [[test]] name = "test_lints" required-features = ["std"] From 0ef9d1b995da1068980762d1be574e7eb965f632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 11 Sep 2024 18:37:33 +0200 Subject: [PATCH 20/24] impl hygiene: fully qualify all paths --- impl/src/expand.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 7c1e351..0c67ad9 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -91,7 +91,7 @@ fn impl_struct(input: Struct) -> TokenStream { let source_method = source_body.map(|body| { quote! { fn source(&self) -> ::core::option::Option<&(dyn ::thiserror::__private::error::Error + 'static)> { - use thiserror::__private::AsDynError as _; + use ::thiserror::__private::AsDynError as _; #body } } @@ -118,32 +118,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 } } @@ -267,8 +267,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)* @@ -304,12 +304,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! { @@ -318,7 +318,7 @@ fn impl_enum(input: Enum) -> TokenStream { #source: #varsource, .. } => { - use thiserror::__private::ThiserrorProvide as _; + use ::thiserror::__private::ThiserrorProvide as _; #source_provide #self_provide } @@ -342,7 +342,7 @@ fn impl_enum(input: Enum) -> TokenStream { }; quote! { #ty::#ident {#backtrace: #varsource, ..} => { - use thiserror::__private::ThiserrorProvide as _; + use ::thiserror::__private::ThiserrorProvide as _; #source_provide } } @@ -352,12 +352,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! { @@ -372,7 +372,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)* @@ -496,7 +496,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 @@ -514,11 +514,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()), } } }); From 7182e3f9df48036ac542bc31c6a7f9a2bb8ef747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 17 Sep 2024 01:39:02 +0200 Subject: [PATCH 21/24] test_no_std: add strictly no_std test Tests source, from, error and hygiene, like test_source. --- tests/test_no_std.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/test_no_std.rs diff --git a/tests/test_no_std.rs b/tests/test_no_std.rs new file mode 100644 index 0000000..4aa00e2 --- /dev/null +++ b/tests/test_no_std.rs @@ -0,0 +1,56 @@ +// There is no negative required-features to suppress this test +// for not(feature = "std"). +#![cfg(not(feature = "std"))] +#![no_std] + +use core::error::Error as _; +use thiserror::Error; + +#[derive(Error, Debug, Default)] +#[error("io")] +pub struct IoError {} + +#[derive(Error, Debug)] +#[error("implicit source")] +pub struct ImplicitSource { + source: IoError, +} + +#[derive(Error, Debug)] +#[error("explicit source")] +pub struct ExplicitSource { + source: i32, + #[source] + io: IoError, +} + +#[test] +fn test_implicit_source() { + let io = IoError::default(); + let error = ImplicitSource { source: io }; + error.source().unwrap().downcast_ref::().unwrap(); +} + +#[test] +fn test_explicit_source() { + let io = IoError::default(); + let error = ExplicitSource { source: 0, io }; + error.source().unwrap().downcast_ref::().unwrap(); +} + +macro_rules! error_from_macro { + ($($variants:tt)*) => { + #[derive(Error)] + #[derive(Debug)] + pub enum MacroSource { + $($variants)* + } + } +} + +// Test that we generate impls with the proper hygiene +#[rustfmt::skip] +error_from_macro! { + #[error("Something")] + Variant(#[from] IoError) +} From 515bd36da54dbc346250026ddc349c88851e4bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 18 Sep 2024 15:31:03 +0200 Subject: [PATCH 22/24] test_{transparent,_lints}: also work with no-std lib --- Cargo.toml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index edf8158..8f20304 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,14 +33,6 @@ rustdoc-args = ["--generate-link-to-definition"] std = [] default = ["std"] -[[test]] -name = "test_lints" -required-features = ["std"] - [[test]] name = "test_path" required-features = ["std"] - -[[test]] -name = "test_transparent" -required-features = ["std"] From 2614b5353c1f9026b1ae999c9439fa14a8b2340f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 18 Sep 2024 15:38:08 +0200 Subject: [PATCH 23/24] test_no_std: reduce --- tests/test_no_std.rs | 55 +++++++++----------------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/tests/test_no_std.rs b/tests/test_no_std.rs index 4aa00e2..cbab7a3 100644 --- a/tests/test_no_std.rs +++ b/tests/test_no_std.rs @@ -1,56 +1,23 @@ -// There is no negative required-features to suppress this test -// for not(feature = "std"). -#![cfg(not(feature = "std"))] #![no_std] - -use core::error::Error as _; use thiserror::Error; -#[derive(Error, Debug, Default)] -#[error("io")] -pub struct IoError {} - #[derive(Error, Debug)] -#[error("implicit source")] -pub struct ImplicitSource { - source: IoError, -} +#[error("io")] +pub struct IoError; #[derive(Error, Debug)] -#[error("explicit source")] -pub struct ExplicitSource { - source: i32, - #[source] - io: IoError, +pub enum MyError { + #[error("A")] + A, + #[error("B {0}")] + B(#[from] IoError), } #[test] -fn test_implicit_source() { - let io = IoError::default(); - let error = ImplicitSource { source: io }; - error.source().unwrap().downcast_ref::().unwrap(); -} +#[cfg_attr(feature = "std", ignore)] +fn test_no_std() { + use core::error::Error as _; -#[test] -fn test_explicit_source() { - let io = IoError::default(); - let error = ExplicitSource { source: 0, io }; + let error = MyError::from(IoError); error.source().unwrap().downcast_ref::().unwrap(); } - -macro_rules! error_from_macro { - ($($variants:tt)*) => { - #[derive(Error)] - #[derive(Debug)] - pub enum MacroSource { - $($variants)* - } - } -} - -// Test that we generate impls with the proper hygiene -#[rustfmt::skip] -error_from_macro! { - #[error("Something")] - Variant(#[from] IoError) -} From 44737a516b7fd0cc9dabcab07e7b1f927f8f5636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 18 Sep 2024 15:46:29 +0200 Subject: [PATCH 24/24] test_no_std: can't compile on old rust --- tests/test_no_std.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_no_std.rs b/tests/test_no_std.rs index cbab7a3..0cb2852 100644 --- a/tests/test_no_std.rs +++ b/tests/test_no_std.rs @@ -14,7 +14,7 @@ pub enum MyError { } #[test] -#[cfg_attr(feature = "std", ignore)] +#[cfg(not(feature = "std"))] fn test_no_std() { use core::error::Error as _;