From 93fae17b3ceed7a3667e3f1a156563c8c93478e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Nov 2021 22:33:37 -0700 Subject: [PATCH 1/5] Support for format args capture --- src/lib.rs | 7 +++++-- src/macros.rs | 14 +++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c19d704..36e87d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -604,9 +604,12 @@ pub trait Context: context::private::Sealed { // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { - pub use alloc::format; + pub use alloc::fmt::format; // the function + pub use alloc::format; // the macro + pub use core::fmt::Arguments; + pub use core::option::Option::Some; pub use core::result::Result::Err; - pub use core::{concat, stringify}; + pub use core::{concat, format_args, stringify}; #[doc(hidden)] pub mod kind { diff --git a/src/macros.rs b/src/macros.rs index 495caa9..8e8c122 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -168,9 +168,17 @@ macro_rules! ensure { #[macro_export] macro_rules! anyhow { ($msg:literal $(,)?) => { - // Handle $:literal as a special case to make cargo-expanded code more - // concise in the common case. - $crate::Error::msg($msg) + match $crate::private::format_args!($crate::private::concat!($msg)) { + arguments => { + if let $crate::private::Some(msg) = $crate::private::Arguments::as_str(&arguments) { + // anyhow!("literal"), can downcast to &'static str + $crate::Error::msg(msg) + } else { + // anyhow!("interpolate {var}"), can downcast to String + $crate::Error::msg($crate::private::format(arguments)) + } + } + } }; ($err:expr $(,)?) => ({ use $crate::private::kind::*; From 9da23ae80b8abb89e71fdae89395719e76587cc5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Nov 2021 23:08:51 -0700 Subject: [PATCH 2/5] Move expansion of anyhow with literal into an inline fn --- src/lib.rs | 22 ++++++++++++++++++---- src/macros.rs | 12 +----------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 36e87d9..2fe6295 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -604,10 +604,11 @@ pub trait Context: context::private::Sealed { // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { - pub use alloc::fmt::format; // the function - pub use alloc::format; // the macro - pub use core::fmt::Arguments; - pub use core::option::Option::Some; + use crate::Error; + use alloc::fmt; + use core::fmt::Arguments; + + pub use alloc::format; pub use core::result::Result::Err; pub use core::{concat, format_args, stringify}; @@ -618,4 +619,17 @@ pub mod private { #[cfg(feature = "std")] pub use crate::kind::BoxedKind; } + + #[doc(hidden)] + #[inline] + #[cold] + pub fn format_err(args: Arguments) -> Error { + if let Some(msg) = args.as_str() { + // anyhow!("literal"), can downcast to &'static str + Error::msg(msg) + } else { + // anyhow!("interpolate {var}"), can downcast to String + Error::msg(fmt::format(args)) + } + } } diff --git a/src/macros.rs b/src/macros.rs index 8e8c122..5c708f4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -168,17 +168,7 @@ macro_rules! ensure { #[macro_export] macro_rules! anyhow { ($msg:literal $(,)?) => { - match $crate::private::format_args!($crate::private::concat!($msg)) { - arguments => { - if let $crate::private::Some(msg) = $crate::private::Arguments::as_str(&arguments) { - // anyhow!("literal"), can downcast to &'static str - $crate::Error::msg(msg) - } else { - // anyhow!("interpolate {var}"), can downcast to String - $crate::Error::msg($crate::private::format(arguments)) - } - } - } + $crate::private::format_err($crate::private::format_args!($crate::private::concat!($msg))) }; ($err:expr $(,)?) => ({ use $crate::private::kind::*; From f8d849b061b449ed0175f95fe1924fa5961ca77d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 2 Nov 2021 16:57:47 -0700 Subject: [PATCH 3/5] Preserve type of non-&str literal passed to format_err --- src/lib.rs | 12 +++++++----- src/macros.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2fe6295..4052cab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -606,7 +606,7 @@ pub trait Context: context::private::Sealed { pub mod private { use crate::Error; use alloc::fmt; - use core::fmt::Arguments; + use core::fmt::{Arguments, Debug, Display}; pub use alloc::format; pub use core::result::Result::Err; @@ -621,12 +621,14 @@ pub mod private { } #[doc(hidden)] - #[inline] #[cold] - pub fn format_err(args: Arguments) -> Error { - if let Some(msg) = args.as_str() { + pub fn format_err(message: M, args: Arguments) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + if args.as_str().is_some() { // anyhow!("literal"), can downcast to &'static str - Error::msg(msg) + Error::msg(message) } else { // anyhow!("interpolate {var}"), can downcast to String Error::msg(fmt::format(args)) diff --git a/src/macros.rs b/src/macros.rs index 5c708f4..5bcab9c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -168,7 +168,7 @@ macro_rules! ensure { #[macro_export] macro_rules! anyhow { ($msg:literal $(,)?) => { - $crate::private::format_err($crate::private::format_args!($crate::private::concat!($msg))) + $crate::private::format_err($msg, $crate::private::format_args!($crate::private::concat!($msg))) }; ($err:expr $(,)?) => ({ use $crate::private::kind::*; From 941d3d6a89ce6d5f6773242402605cf45768b083 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 2 Nov 2021 17:01:49 -0700 Subject: [PATCH 4/5] Eliminate Arguments::as_str call on old toolchain --- build.rs | 4 ++++ src/lib.rs | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build.rs b/build.rs index a12b550..cd79927 100644 --- a/build.rs +++ b/build.rs @@ -58,6 +58,10 @@ fn main() { if rustc < 51 { println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of"); } + + if rustc < 52 { + println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str"); + } } fn compile_probe() -> Option { diff --git a/src/lib.rs b/src/lib.rs index 4052cab..b227eef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -626,12 +626,17 @@ pub mod private { where M: Display + Debug + Send + Sync + 'static, { - if args.as_str().is_some() { - // anyhow!("literal"), can downcast to &'static str - Error::msg(message) - } else { + #[cfg(anyhow_no_fmt_arguments_as_str)] + let has_interpolated_format_args = false; + #[cfg(not(anyhow_no_fmt_arguments_as_str))] + let has_interpolated_format_args = args.as_str().is_none(); + + if has_interpolated_format_args { // anyhow!("interpolate {var}"), can downcast to String Error::msg(fmt::format(args)) + } else { + // anyhow!("literal"), can downcast to &'static str + Error::msg(message) } } } From 6ae993736ce2903c169aebfcc55e7d15299cffe5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 2 Nov 2021 19:17:03 -0700 Subject: [PATCH 5/5] Remove workaround for nonstring literals Stringifying a non-string $:literal for passing into the format_args call is incompatible with format args capture: "to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro". error: there is no argument named `var`" --> src/main.rs:3:26 | 3 | let _ = format_args!(concat!("{var}")); | ^^^^^^^^^^^^^^^^ | = note: did you intend to capture a variable `var` from the surrounding scope? = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 5bcab9c..91e433b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -168,7 +168,7 @@ macro_rules! ensure { #[macro_export] macro_rules! anyhow { ($msg:literal $(,)?) => { - $crate::private::format_err($msg, $crate::private::format_args!($crate::private::concat!($msg))) + $crate::private::format_err($msg, $crate::private::format_args!($msg)) }; ($err:expr $(,)?) => ({ use $crate::private::kind::*;