diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b78da9..b8d5d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [0.16.0] - 2025-09-26 + +### Changed +- Switched the internal `AppError` source storage to `Arc` and added a + shared `with_source_arc` helper so conversions can reuse existing `Arc` + handles without extra allocations. +- Replaced the backtrace slot with an `Option` managed through an + environment-aware lazy capture that respects `RUST_BACKTRACE` and avoids + snapshot allocation when disabled. +- Updated the `masterror::Error` derive and `ResultExt` conversions to forward + sources using the new shared storage while preserving error chains. + +### Tests +- Added regression coverage for the `std::error::Error` chain, `Arc` source + preservation in the derives, and conditional backtrace capture driven by the + `RUST_BACKTRACE` environment variable. + ## [0.15.0] - 2025-09-25 ### Added diff --git a/Cargo.lock b/Cargo.lock index fe11b51..6460182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1727,7 +1727,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.15.0" +version = "0.16.0" dependencies = [ "actix-web", "axum 0.8.4", @@ -1763,7 +1763,7 @@ dependencies = [ [[package]] name = "masterror-derive" -version = "0.7.0" +version = "0.7.1" dependencies = [ "masterror-template", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 2d41449..5de95af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masterror" -version = "0.15.0" +version = "0.16.0" rust-version = "1.90" edition = "2024" license = "MIT OR Apache-2.0" @@ -75,7 +75,7 @@ tonic = ["dep:tonic"] openapi = ["dep:utoipa"] [workspace.dependencies] -masterror-derive = { version = "0.7.0" } +masterror-derive = { version = "0.7.1" } masterror-template = { version = "0.3.6" } [dependencies] diff --git a/README.md b/README.md index 74a5e9e..0a8970a 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,9 @@ guides, comparisons with `thiserror`/`anyhow`, and troubleshooting recipes. ~~~toml [dependencies] -masterror = { version = "0.15.0", default-features = false } +masterror = { version = "0.16.0", default-features = false } # or with features: -# masterror = { version = "0.15.0", features = [ - +# masterror = { version = "0.16.0", features = [ # "axum", "actix", "openapi", "serde_json", # "tracing", "metrics", "backtrace", "sqlx", # "sqlx-migrate", "reqwest", "redis", "validator", @@ -79,10 +78,10 @@ masterror = { version = "0.15.0", default-features = false } ~~~toml [dependencies] # lean core -masterror = { version = "0.15.0", default-features = false } +masterror = { version = "0.16.0", default-features = false } # with Axum/Actix + JSON + integrations -# masterror = { version = "0.15.0", features = [ +# masterror = { version = "0.16.0", features = [ # "axum", "actix", "openapi", "serde_json", # "tracing", "metrics", "backtrace", "sqlx", # "sqlx-migrate", "reqwest", "redis", "validator", @@ -720,13 +719,13 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); Minimal core: ~~~toml -masterror = { version = "0.15.0", default-features = false } +masterror = { version = "0.16.0", default-features = false } ~~~ API (Axum + JSON + deps): ~~~toml -masterror = { version = "0.15.0", features = [ +masterror = { version = "0.16.0", features = [ "axum", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } @@ -735,7 +734,7 @@ masterror = { version = "0.15.0", features = [ API (Actix + JSON + deps): ~~~toml -masterror = { version = "0.15.0", features = [ +masterror = { version = "0.16.0", features = [ "actix", "serde_json", "openapi", "sqlx", "reqwest", "redis", "validator", "config", "tokio" ] } diff --git a/masterror-derive/Cargo.toml b/masterror-derive/Cargo.toml index dadb1cd..797d219 100644 --- a/masterror-derive/Cargo.toml +++ b/masterror-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "masterror-derive" rust-version = "1.90" -version = "0.7.0" +version = "0.7.1" edition = "2024" license = "MIT OR Apache-2.0" repository = "https://github.com/RAprogramm/masterror" diff --git a/masterror-derive/src/input.rs b/masterror-derive/src/input.rs index 64a00e0..adedc94 100644 --- a/masterror-derive/src/input.rs +++ b/masterror-derive/src/input.rs @@ -1509,6 +1509,19 @@ pub(crate) fn option_inner_type(ty: &syn::Type) -> Option<&syn::Type> { }) } +pub(crate) fn is_arc_type(ty: &syn::Type) -> bool { + let syn::Type::Path(path) = ty else { + return false; + }; + if path.qself.is_some() { + return false; + } + path.path + .segments + .last() + .is_some_and(|segment| segment.ident == "Arc") +} + pub(crate) fn is_backtrace_type(ty: &syn::Type) -> bool { let syn::Type::Path(path) = ty else { return false; diff --git a/masterror-derive/src/masterror_impl.rs b/masterror-derive/src/masterror_impl.rs index e51b7d0..e16145e 100644 --- a/masterror-derive/src/masterror_impl.rs +++ b/masterror-derive/src/masterror_impl.rs @@ -3,7 +3,8 @@ use quote::{format_ident, quote}; use syn::{Error, Expr, ExprPath, Index}; use crate::input::{ - ErrorData, ErrorInput, Field, Fields, MasterrorSpec, StructData, VariantData, is_option_type + ErrorData, ErrorInput, Field, Fields, MasterrorSpec, StructData, VariantData, is_arc_type, + is_option_type, option_inner_type }; pub fn expand(input: &ErrorInput) -> Result { @@ -433,13 +434,27 @@ fn source_attachment_tokens(bound_fields: &[BoundField<'_>]) -> TokenStream { for bound in bound_fields { if bound.field.attrs.has_source() { let binding = &bound.binding; - if is_option_type(&bound.field.ty) { + let ty = &bound.field.ty; + if is_option_type(ty) { + let arc_inner = option_inner_type(ty).is_some_and(is_arc_type); + if arc_inner { + return quote! { + if let Some(source) = #binding { + __masterror_error = __masterror_error.with_source_arc(source); + } + }; + } return quote! { if let Some(source) = #binding { __masterror_error = __masterror_error.with_source(source); } }; } else { + if is_arc_type(ty) { + return quote! { + __masterror_error = __masterror_error.with_source_arc(#binding); + }; + } return quote! { __masterror_error = __masterror_error.with_source(#binding); }; diff --git a/src/app_error/core.rs b/src/app_error/core.rs index c6b6055..7284192 100644 --- a/src/app_error/core.rs +++ b/src/app_error/core.rs @@ -1,12 +1,21 @@ #[cfg(feature = "backtrace")] -use std::sync::OnceLock; use std::{ backtrace::Backtrace, + env, + sync::{ + OnceLock, + atomic::{AtomicU8, Ordering as AtomicOrdering} + } +}; +use std::{ borrow::Cow, error::Error as StdError, fmt::{Display, Formatter, Result as FmtResult}, ops::{Deref, DerefMut}, - sync::atomic::{AtomicBool, Ordering} + sync::{ + Arc, + atomic::{AtomicBool, Ordering} + } }; #[cfg(feature = "tracing")] @@ -50,9 +59,7 @@ impl BacktraceSlot { } fn capture_if_absent(&self) -> Option<&Backtrace> { - self.cell - .get_or_init(|| Some(Backtrace::capture())) - .as_ref() + self.cell.get_or_init(capture_backtrace_snapshot).as_ref() } } @@ -64,7 +71,19 @@ impl Default for BacktraceSlot { } #[cfg(not(feature = "backtrace"))] -type BacktraceSlot = Option; +#[derive(Debug, Default)] +struct BacktraceSlot { + _marker: () +} + +#[cfg(not(feature = "backtrace"))] +impl BacktraceSlot { + fn set(&mut self, _backtrace: std::backtrace::Backtrace) {} + + fn capture_if_absent(&self) -> Option<&std::backtrace::Backtrace> { + None + } +} #[derive(Debug)] #[doc(hidden)] @@ -83,11 +102,71 @@ pub struct ErrorInner { pub retry: Option, /// Optional authentication challenge for `WWW-Authenticate`. pub www_authenticate: Option, - source: Option>, + source: Option>, backtrace: BacktraceSlot, telemetry_dirty: AtomicBool } +#[cfg(feature = "backtrace")] +const BACKTRACE_STATE_UNSET: u8 = 0; +#[cfg(feature = "backtrace")] +const BACKTRACE_STATE_ENABLED: u8 = 1; +#[cfg(feature = "backtrace")] +const BACKTRACE_STATE_DISABLED: u8 = 2; + +#[cfg(feature = "backtrace")] +static BACKTRACE_STATE: AtomicU8 = AtomicU8::new(BACKTRACE_STATE_UNSET); + +#[cfg(feature = "backtrace")] +fn capture_backtrace_snapshot() -> Option { + if should_capture_backtrace() { + Some(Backtrace::capture()) + } else { + None + } +} + +#[cfg(feature = "backtrace")] +fn should_capture_backtrace() -> bool { + match BACKTRACE_STATE.load(AtomicOrdering::Acquire) { + BACKTRACE_STATE_ENABLED => true, + BACKTRACE_STATE_DISABLED => false, + _ => { + let enabled = detect_backtrace_preference(); + BACKTRACE_STATE.store( + if enabled { + BACKTRACE_STATE_ENABLED + } else { + BACKTRACE_STATE_DISABLED + }, + AtomicOrdering::Release + ); + enabled + } + } +} + +#[cfg(feature = "backtrace")] +fn detect_backtrace_preference() -> bool { + match env::var_os("RUST_BACKTRACE") { + None => false, + Some(value) => { + let value = value.to_string_lossy(); + let trimmed = value.trim(); + if trimmed.is_empty() { + return false; + } + let lowered = trimmed.to_ascii_lowercase(); + !(matches!(lowered.as_str(), "0" | "off" | "false")) + } + } +} + +#[cfg(all(test, feature = "backtrace"))] +pub(crate) fn reset_backtrace_preference() { + BACKTRACE_STATE.store(BACKTRACE_STATE_UNSET, AtomicOrdering::Release); +} + /// Rich application error preserving domain code, taxonomy and metadata. #[derive(Debug)] pub struct Error { @@ -117,8 +196,13 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { self.source - .as_ref() - .map(|source| &**source as &(dyn StdError + 'static)) + .as_deref() + .map(|source| source as &(dyn StdError + 'static)) + } + + #[cfg(feature = "backtrace")] + fn backtrace(&self) -> Option<&Backtrace> { + self.capture_backtrace() } } @@ -173,26 +257,14 @@ impl Error { self.telemetry_dirty.swap(false, Ordering::AcqRel) } - #[cfg(feature = "backtrace")] - fn capture_backtrace(&self) -> Option<&Backtrace> { + fn capture_backtrace(&self) -> Option<&std::backtrace::Backtrace> { self.backtrace.capture_if_absent() } - #[cfg(not(feature = "backtrace"))] - fn capture_backtrace(&self) -> Option<&Backtrace> { - self.backtrace.as_ref() - } - - #[cfg(feature = "backtrace")] - fn set_backtrace_slot(&mut self, backtrace: Backtrace) { + fn set_backtrace_slot(&mut self, backtrace: std::backtrace::Backtrace) { self.backtrace.set(backtrace); } - #[cfg(not(feature = "backtrace"))] - fn set_backtrace_slot(&mut self, backtrace: Backtrace) { - self.backtrace = Some(backtrace); - } - pub(crate) fn emit_telemetry(&self) { if self.take_dirty() { #[cfg(feature = "backtrace")] @@ -331,14 +403,35 @@ impl Error { /// Attach a source error for diagnostics. #[must_use] pub fn with_source(mut self, source: impl StdError + Send + Sync + 'static) -> Self { - self.source = Some(Box::new(source)); + self.source = Some(Arc::new(source)); + self.mark_dirty(); + self + } + + /// Attach a shared source error without cloning the underlying `Arc`. + /// + /// # Examples + /// + /// ```rust + /// use std::sync::Arc; + /// + /// use masterror::{AppError, AppErrorKind}; + /// + /// let source = Arc::new(std::io::Error::new(std::io::ErrorKind::Other, "boom")); + /// let err = AppError::internal("boom").with_source_arc(source.clone()); + /// assert!(err.source_ref().is_some()); + /// assert_eq!(Arc::strong_count(&source), 2); + /// ``` + #[must_use] + pub fn with_source_arc(mut self, source: Arc) -> Self { + self.source = Some(source); self.mark_dirty(); self } /// Attach a captured backtrace. #[must_use] - pub fn with_backtrace(mut self, backtrace: Backtrace) -> Self { + pub fn with_backtrace(mut self, backtrace: std::backtrace::Backtrace) -> Self { self.set_backtrace_slot(backtrace); self.mark_dirty(); self @@ -353,7 +446,7 @@ impl Error { /// Borrow the backtrace, capturing it lazily when the `backtrace` feature /// is enabled. #[must_use] - pub fn backtrace(&self) -> Option<&Backtrace> { + pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> { self.capture_backtrace() } diff --git a/src/app_error/tests.rs b/src/app_error/tests.rs index 03f2c66..0ca0ec6 100644 --- a/src/app_error/tests.rs +++ b/src/app_error/tests.rs @@ -1,4 +1,12 @@ use std::{borrow::Cow, error::Error as StdError, fmt::Display, sync::Arc}; +#[cfg(feature = "backtrace")] +use std::{env, sync::Mutex}; + +#[cfg(feature = "backtrace")] +use super::core::reset_backtrace_preference; + +#[cfg(feature = "backtrace")] +static BACKTRACE_ENV_GUARD: Mutex<()> = Mutex::new(()); use super::{AppError, FieldValue, MessageEditPolicy, field}; use crate::{AppCode, AppErrorKind}; @@ -176,15 +184,74 @@ impl StdError for DummyError {} #[test] fn source_is_preserved_without_extra_allocation() { let source = Arc::new(DummyError); - let err = AppError::internal("boom").with_source(source.clone()); + let err = AppError::internal("boom").with_source_arc(source.clone()); assert_eq!(Arc::strong_count(&source), 2); let stored = err.source_ref().expect("source"); - let stored_arc = stored - .downcast_ref::>() - .expect("arc should be preserved"); - assert!(Arc::ptr_eq(stored_arc, &source)); + let stored_dummy = stored + .downcast_ref::() + .expect("dummy should be preserved"); + assert!(std::ptr::eq(stored_dummy, &*source)); +} + +#[test] +fn error_chain_is_preserved() { + #[derive(Debug)] + struct NestedError { + inner: DummyError + } + + impl Display for NestedError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } + } + + impl StdError for NestedError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.inner) + } + } + + let err = AppError::internal("boom").with_source(NestedError { + inner: DummyError + }); + let top_source = StdError::source(&err).expect("top source"); + assert!(top_source.is::()); + let nested = top_source.source().expect("nested source"); + assert!(nested.is::()); +} + +#[cfg(feature = "backtrace")] +fn with_backtrace_env(value: Option<&str>, test: F) { + let _guard = BACKTRACE_ENV_GUARD.lock().expect("env guard"); + reset_backtrace_preference(); + match value { + Some(val) => env::set_var("RUST_BACKTRACE", val), + None => env::remove_var("RUST_BACKTRACE") + } + test(); + env::remove_var("RUST_BACKTRACE"); + reset_backtrace_preference(); +} + +#[cfg(feature = "backtrace")] +#[test] +fn backtrace_respects_disabled_env() { + with_backtrace_env(Some("0"), || { + let err = AppError::internal("boom"); + assert!(err.backtrace().is_none()); + }); +} + +#[cfg(feature = "backtrace")] +#[test] +fn backtrace_enabled_when_env_requests() { + with_backtrace_env(Some("1"), || { + let err = AppError::internal("boom"); + assert!(err.backtrace().is_some()); + }); } #[test] diff --git a/src/lib.rs b/src/lib.rs index f10b00f..825b779 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,8 +38,8 @@ //! with RFC7807 body //! - `actix` — implements `Responder` for [`ProblemJson`] and Actix //! `ResponseError` for [`AppError`] -//! - `tonic` — converts [`struct@Error`] into `tonic::Status` with -//! sanitized metadata +//! - `tonic` — converts [`struct@Error`] into `tonic::Status` with sanitized +//! metadata //! - `openapi` — derives an OpenAPI schema for [`ErrorResponse`] (via `utoipa`) //! - `sqlx` — `From` mapping //! - `redis` — `From` mapping diff --git a/src/response/problem_json.rs b/src/response/problem_json.rs index d7adee6..28af7e7 100644 --- a/src/response/problem_json.rs +++ b/src/response/problem_json.rs @@ -138,19 +138,16 @@ impl ProblemJson { /// assert_eq!(problem.status, 409); /// ``` #[must_use] - pub fn from_app_error(error: AppError) -> Self { - let err = error; - err.emit_telemetry(); - let AppError { - code, - kind, - message, - metadata, - edit_policy, - retry, - www_authenticate, - .. - } = err; + pub fn from_app_error(mut error: AppError) -> Self { + error.emit_telemetry(); + + let code = error.code; + let kind = error.kind; + let message = error.message.take(); + let metadata = core::mem::take(&mut error.metadata); + let edit_policy = error.edit_policy; + let retry = error.retry.take(); + let www_authenticate = error.www_authenticate.take(); let mapping = mapping_for_code(code); let status = kind.http_status(); diff --git a/tests/masterror_macro.rs b/tests/masterror_macro.rs index e9815e0..668b340 100644 --- a/tests/masterror_macro.rs +++ b/tests/masterror_macro.rs @@ -1,5 +1,7 @@ #![allow(non_shorthand_field_patterns)] +use std::sync::Arc; + use masterror::{ AppCode, AppErrorKind, Error as MasterrorError, Masterror, MessageEditPolicy, mapping::{GrpcMapping, HttpMapping, ProblemMapping} @@ -155,3 +157,42 @@ fn enum_masterror_conversion_handles_variants() { )] ); } + +#[test] +fn masterror_preserves_arc_source_without_extra_clone() { + let source = Arc::new(ArcLeafError); + let converted: MasterrorError = ArcSourceError { + source: source.clone() + } + .into(); + + assert_eq!(Arc::strong_count(&source), 2); + + let stored = converted + .source_ref() + .and_then(|src| src.downcast_ref::()) + .expect("arc source"); + assert!(std::ptr::eq(stored, &*source)); +} +#[derive(Debug)] +struct ArcLeafError; + +impl std::fmt::Display for ArcLeafError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("arc leaf") + } +} + +impl std::error::Error for ArcLeafError {} + +#[derive(Debug, Masterror)] +#[error("arc leaf source")] +#[masterror( + code = AppCode::Internal, + category = AppErrorKind::Internal, + message +)] +struct ArcSourceError { + #[source] + source: Arc +} diff --git a/tests/ui/app_error/fail/enum_missing_variant.stderr b/tests/ui/app_error/fail/enum_missing_variant.stderr index bbc297c..d000de1 100644 --- a/tests/ui/app_error/fail/enum_missing_variant.stderr +++ b/tests/ui/app_error/fail/enum_missing_variant.stderr @@ -1,9 +1,8 @@ error: all variants must use #[app_error(...)] to derive AppError conversion --> tests/ui/app_error/fail/enum_missing_variant.rs:8:5 | -8 | / #[error("without")] -9 | | Without, - | |___________^ +8 | #[error("without")] + | ^ warning: unused import: `AppErrorKind` --> tests/ui/app_error/fail/enum_missing_variant.rs:1:17 @@ -11,4 +10,4 @@ warning: unused import: `AppErrorKind` 1 | use masterror::{AppErrorKind, Error}; | ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/app_error/fail/missing_code.stderr b/tests/ui/app_error/fail/missing_code.stderr index 4f02301..70ccade 100644 --- a/tests/ui/app_error/fail/missing_code.stderr +++ b/tests/ui/app_error/fail/missing_code.stderr @@ -2,7 +2,7 @@ error: AppCode conversion requires `code = ...` in #[app_error(...)] --> tests/ui/app_error/fail/missing_code.rs:9:5 | 9 | #[app_error(kind = AppErrorKind::Service)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ warning: unused imports: `AppCode` and `AppErrorKind` --> tests/ui/app_error/fail/missing_code.rs:1:17 @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Error}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/app_error/fail/missing_kind.stderr b/tests/ui/app_error/fail/missing_kind.stderr index 021c135..c615e98 100644 --- a/tests/ui/app_error/fail/missing_kind.stderr +++ b/tests/ui/app_error/fail/missing_kind.stderr @@ -2,4 +2,4 @@ error: missing `kind = ...` in #[app_error(...)] --> tests/ui/app_error/fail/missing_kind.rs:5:1 | 5 | #[app_error(message)] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^ diff --git a/tests/ui/formatter/fail/duplicate_fmt.stderr b/tests/ui/formatter/fail/duplicate_fmt.stderr index 5b8f363..5b08225 100644 --- a/tests/ui/formatter/fail/duplicate_fmt.stderr +++ b/tests/ui/formatter/fail/duplicate_fmt.stderr @@ -2,4 +2,4 @@ error: duplicate `fmt` handler specified --> tests/ui/formatter/fail/duplicate_fmt.rs:4:36 | 4 | #[error(fmt = crate::format_error, fmt = crate::format_error)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ diff --git a/tests/ui/formatter/fail/implicit_after_named.stderr b/tests/ui/formatter/fail/implicit_after_named.stderr index be76742..d416399 100644 --- a/tests/ui/formatter/fail/implicit_after_named.stderr +++ b/tests/ui/formatter/fail/implicit_after_named.stderr @@ -8,5 +8,4 @@ error: multiple unused formatting arguments | argument never used | argument never used | - = note: consider adding 2 format specifiers = note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/formatter/fail/unsupported_flag.stderr b/tests/ui/formatter/fail/unsupported_flag.stderr index b8bf229..d7acdb1 100644 --- a/tests/ui/formatter/fail/unsupported_flag.stderr +++ b/tests/ui/formatter/fail/unsupported_flag.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..11 uses an unsupported formatter - --> tests/ui/formatter/fail/unsupported_flag.rs:4:10 + --> tests/ui/formatter/fail/unsupported_flag.rs:4:9 | 4 | #[error("{value:##x}")] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/unsupported_formatter.stderr b/tests/ui/formatter/fail/unsupported_formatter.stderr index a6a40c2..5869420 100644 --- a/tests/ui/formatter/fail/unsupported_formatter.stderr +++ b/tests/ui/formatter/fail/unsupported_formatter.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..9 uses an unsupported formatter - --> tests/ui/formatter/fail/unsupported_formatter.rs:4:10 + --> tests/ui/formatter/fail/unsupported_formatter.rs:4:9 | 4 | #[error("{value:y}")] - | ^^^^^^^^^ + | ^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/uppercase_binary.stderr b/tests/ui/formatter/fail/uppercase_binary.stderr index 3d332c7..bbe04b4 100644 --- a/tests/ui/formatter/fail/uppercase_binary.stderr +++ b/tests/ui/formatter/fail/uppercase_binary.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..9 uses an unsupported formatter - --> tests/ui/formatter/fail/uppercase_binary.rs:4:10 + --> tests/ui/formatter/fail/uppercase_binary.rs:4:9 | 4 | #[error("{value:B}")] - | ^^^^^^^^^ + | ^^^^^^^^^^^ diff --git a/tests/ui/formatter/fail/uppercase_pointer.stderr b/tests/ui/formatter/fail/uppercase_pointer.stderr index 0bd10fa..2c30e71 100644 --- a/tests/ui/formatter/fail/uppercase_pointer.stderr +++ b/tests/ui/formatter/fail/uppercase_pointer.stderr @@ -1,5 +1,5 @@ error: placeholder spanning bytes 0..9 uses an unsupported formatter - --> tests/ui/formatter/fail/uppercase_pointer.rs:4:10 + --> tests/ui/formatter/fail/uppercase_pointer.rs:4:9 | 4 | #[error("{value:P}")] - | ^^^^^^^^^ + | ^^^^^^^^^^^ diff --git a/tests/ui/masterror/fail/duplicate_attr.stderr b/tests/ui/masterror/fail/duplicate_attr.stderr index 113a10d..c3fb86b 100644 --- a/tests/ui/masterror/fail/duplicate_attr.stderr +++ b/tests/ui/masterror/fail/duplicate_attr.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/duplicate_telemetry.stderr b/tests/ui/masterror/fail/duplicate_telemetry.stderr index 9ada290..b331baa 100644 --- a/tests/ui/masterror/fail/duplicate_telemetry.stderr +++ b/tests/ui/masterror/fail/duplicate_telemetry.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/empty_redact.stderr b/tests/ui/masterror/fail/empty_redact.stderr index fd151cc..b2658a1 100644 --- a/tests/ui/masterror/fail/empty_redact.stderr +++ b/tests/ui/masterror/fail/empty_redact.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/enum_missing_variant.stderr b/tests/ui/masterror/fail/enum_missing_variant.stderr index 5a25e12..83d517f 100644 --- a/tests/ui/masterror/fail/enum_missing_variant.stderr +++ b/tests/ui/masterror/fail/enum_missing_variant.stderr @@ -1,9 +1,8 @@ error: all variants must use #[masterror(...)] to derive masterror::Error conversion --> tests/ui/masterror/fail/enum_missing_variant.rs:8:5 | -8 | / #[error("missing")] -9 | | Missing - | |___________^ +8 | #[error("missing")] + | ^ warning: unused imports: `AppCode` and `AppErrorKind` --> tests/ui/masterror/fail/enum_missing_variant.rs:1:17 @@ -11,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/missing_category.stderr b/tests/ui/masterror/fail/missing_category.stderr index bdadf45..f929951 100644 --- a/tests/ui/masterror/fail/missing_category.stderr +++ b/tests/ui/masterror/fail/missing_category.stderr @@ -2,7 +2,7 @@ error: missing `category = ...` in #[masterror(...)] --> tests/ui/masterror/fail/missing_category.rs:5:1 | 5 | #[masterror(code = AppCode::Internal)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ warning: unused import: `AppCode` --> tests/ui/masterror/fail/missing_category.rs:1:17 @@ -10,4 +10,4 @@ warning: unused import: `AppCode` 1 | use masterror::{AppCode, Masterror}; | ^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/missing_code.stderr b/tests/ui/masterror/fail/missing_code.stderr index 037fac8..34abc91 100644 --- a/tests/ui/masterror/fail/missing_code.stderr +++ b/tests/ui/masterror/fail/missing_code.stderr @@ -2,7 +2,7 @@ error: missing `code = ...` in #[masterror(...)] --> tests/ui/masterror/fail/missing_code.rs:5:1 | 5 | #[masterror(category = AppErrorKind::Internal)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ warning: unused import: `AppErrorKind` --> tests/ui/masterror/fail/missing_code.rs:1:17 @@ -10,4 +10,4 @@ warning: unused import: `AppErrorKind` 1 | use masterror::{AppErrorKind, Masterror}; | ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default diff --git a/tests/ui/masterror/fail/unknown_option.stderr b/tests/ui/masterror/fail/unknown_option.stderr index 1822edf..d579838 100644 --- a/tests/ui/masterror/fail/unknown_option.stderr +++ b/tests/ui/masterror/fail/unknown_option.stderr @@ -10,4 +10,4 @@ warning: unused imports: `AppCode` and `AppErrorKind` 1 | use masterror::{AppCode, AppErrorKind, Masterror}; | ^^^^^^^ ^^^^^^^^^^^^ | - = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + = note: `#[warn(unused_imports)]` on by default