Skip to content

Commit

Permalink
feat: override the eyre display handler globally (#9766)
Browse files Browse the repository at this point in the history
* feat: override the eyre display handler globally

* chore: install handler in anvil

* msg
  • Loading branch information
DaniPopes authored Jan 27, 2025
1 parent 5261dc1 commit 66e3648
Show file tree
Hide file tree
Showing 12 changed files with 72 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
msrv = "1.83"
# bytes::Bytes is included by default and alloy_primitives::Bytes is a wrapper around it,
# so it is safe to ignore it as well

# `bytes::Bytes` is included by default and `alloy_primitives::Bytes` is a wrapper around it,
# so it is safe to ignore it as well.
ignore-interior-mutability = ["bytes::Bytes", "alloy_primitives::Bytes"]

disallowed-macros = [
# See `foundry_common::shell`
# See `foundry_common::shell`.
{ path = "std::print", reason = "use `sh_print` or similar macros instead" },
{ path = "std::eprint", reason = "use `sh_eprint` or similar macros instead" },
{ path = "std::println", reason = "use `sh_println` or similar macros instead" },
Expand Down
4 changes: 3 additions & 1 deletion crates/anvil/src/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use anvil::cmd::NodeArgs;
use clap::{CommandFactory, Parser, Subcommand};
use eyre::Result;
use foundry_cli::{opts::GlobalArgs, utils};
use foundry_cli::{handler, opts::GlobalArgs, utils};
use foundry_common::version::{LONG_VERSION, SHORT_VERSION};

#[cfg(all(feature = "jemalloc", unix))]
Expand Down Expand Up @@ -47,7 +47,9 @@ fn main() {
}

fn run() -> Result<()> {
handler::install();
utils::load_dotenv();
utils::enable_paint();

let mut args = Anvil::parse();
args.global.init()?;
Expand Down
7 changes: 1 addition & 6 deletions crates/cheatcodes/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ impl_from!(
alloy_sol_types::Error,
alloy_dyn_abi::Error,
alloy_primitives::SignatureError,
eyre::Report,
FsPathError,
hex::FromHexError,
BackendError,
Expand All @@ -306,12 +307,6 @@ impl<T: Into<BackendError>> From<EVMError<T>> for Error {
}
}

impl From<eyre::Report> for Error {
fn from(err: eyre::Report) -> Self {
Self::from(foundry_common::errors::display_chain(&err))
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dotenvy = "0.15"
eyre.workspace = true
futures.workspace = true
indicatif = "0.17"
itertools.workspace = true
rayon.workspace = true
regex = { workspace = true, default-features = false }
serde_json.workspace = true
Expand Down
57 changes: 46 additions & 11 deletions crates/cli/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,42 @@
use eyre::EyreHandler;
use itertools::Itertools;
use std::{error::Error, fmt};

/// A custom context type for Foundry specific error reporting via `eyre`
#[derive(Debug)]
pub struct Handler;
/// A custom context type for Foundry specific error reporting via `eyre`.
pub struct Handler {
debug_handler: Option<Box<dyn EyreHandler>>,
}

impl Default for Handler {
fn default() -> Self {
Self::new()
}
}

impl Handler {
/// Create a new instance of the `Handler`.
pub fn new() -> Self {
Self { debug_handler: None }
}

/// Override the debug handler with a custom one.
pub fn debug_handler(mut self, debug_handler: Option<Box<dyn EyreHandler>>) -> Self {
self.debug_handler = debug_handler;
self
}
}

impl EyreHandler for Handler {
fn display(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
use fmt::Display;
foundry_common::errors::dedup_chain(error).into_iter().format("; ").fmt(f)
}

fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(debug_handler) = &self.debug_handler {
return debug_handler.debug(error, f);
}

if f.alternate() {
return fmt::Debug::fmt(error, f)
}
Expand All @@ -31,9 +61,15 @@ impl EyreHandler for Handler {

Ok(())
}

fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {
if let Some(debug_handler) = &mut self.debug_handler {
debug_handler.track_caller(location);
}
}
}

/// Installs the Foundry [eyre] and [panic](mod@std::panic) hooks as the global ones.
/// Installs the Foundry [`eyre`] and [`panic`](mod@std::panic) hooks as the global ones.
///
/// # Details
///
Expand All @@ -49,15 +85,14 @@ pub fn install() {

let panic_section =
"This is a bug. Consider reporting it at https://github.com/foundry-rs/foundry";
let (panic_hook, debug_eyre_hook) =
let (panic_hook, debug_hook) =
color_eyre::config::HookBuilder::default().panic_section(panic_section).into_hooks();
panic_hook.install();
let eyre_install_result = if std::env::var_os("FOUNDRY_DEBUG").is_some() {
debug_eyre_hook.install()
} else {
eyre::set_hook(Box::new(|_| Box::new(Handler)))
};
if let Err(e) = eyre_install_result {
let debug_hook = debug_hook.into_eyre_hook();
let debug = std::env::var_os("FOUNDRY_DEBUG").is_some();
if let Err(e) = eyre::set_hook(Box::new(move |e| {
Box::new(Handler::new().debug_handler(debug.then(|| debug_hook(e))))
})) {
debug!("failed to install eyre error hook: {e}");
}
}
3 changes: 1 addition & 2 deletions crates/common/src/retry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ impl Retry {

fn log(&self, err: Error, warn: bool) {
let msg = format!(
"{msg}{delay} ({retries} tries remaining)",
msg = crate::errors::display_chain(&err),
"{err}{delay} ({retries} tries remaining)",
delay = if self.delay.is_zero() {
String::new()
} else {
Expand Down
9 changes: 4 additions & 5 deletions crates/evm/core/src/fork/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub async fn environment<N: Network, T: Transport + Clone, P: Provider<T, N>>(
let block_number = if let Some(pin_block) = pin_block {
pin_block
} else {
provider.get_block_number().await.wrap_err("Failed to get latest block number")?
provider.get_block_number().await.wrap_err("failed to get latest block number")?
};
let (fork_gas_price, rpc_chain_id, block) = tokio::try_join!(
provider.get_gas_price(),
Expand All @@ -44,12 +44,11 @@ pub async fn environment<N: Network, T: Transport + Clone, P: Provider<T, N>>(
error!("{NON_ARCHIVE_NODE_WARNING}");
}
eyre::bail!(
"Failed to get block for block number: {}\nlatest block number: {}",
block_number,
latest_block
"failed to get block for block number: {block_number}; \
latest block number: {latest_block}"
);
}
eyre::bail!("Failed to get block for block number: {}", block_number)
eyre::bail!("failed to get block for block number: {block_number}")
};

let mut cfg = CfgEnv::default();
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/core/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl EvmOpts {
)
.await
.wrap_err_with(|| {
let mut msg = "Could not instantiate forked environment".to_string();
let mut msg = "could not instantiate forked environment".to_string();
if let Ok(url) = Url::parse(fork_url) {
if let Some(provider) = url.host() {
write!(msg, " with provider {provider}").unwrap();
Expand Down
14 changes: 7 additions & 7 deletions crates/evm/evm/src/executors/invariant/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ impl<'a> InvariantExecutor<'a> {

// We stop the run immediately if we have reverted, and `fail_on_revert` is set.
if self.config.fail_on_revert && invariant_test.reverts() > 0 {
return Err(TestCaseError::fail("Revert occurred."))
return Err(TestCaseError::fail("call reverted"))
}

while current_run.depth < self.config.depth {
Expand All @@ -362,7 +362,7 @@ impl<'a> InvariantExecutor<'a> {
}

let tx = current_run.inputs.last().ok_or_else(|| {
TestCaseError::fail("No input generated to call fuzzed target.")
TestCaseError::fail("no input generated to called fuzz target")
})?;

// Execute call from the randomly generated sequence without committing state.
Expand All @@ -375,9 +375,7 @@ impl<'a> InvariantExecutor<'a> {
tx.call_details.calldata.clone(),
U256::ZERO,
)
.map_err(|e| {
TestCaseError::fail(format!("Could not make raw evm call: {e}"))
})?;
.map_err(|e| TestCaseError::fail(e.to_string()))?;

let discarded = call_result.result.as_ref() == MAGIC_ASSUME;
if self.config.show_metrics {
Expand All @@ -394,7 +392,9 @@ impl<'a> InvariantExecutor<'a> {
invariant_test.set_error(InvariantFuzzError::MaxAssumeRejects(
self.config.max_assume_rejects,
));
return Err(TestCaseError::fail("Max number of vm.assume rejects reached."))
return Err(TestCaseError::fail(
"reached maximum number of `vm.assume` rejects",
));
}
} else {
// Commit executed call result.
Expand Down Expand Up @@ -446,7 +446,7 @@ impl<'a> InvariantExecutor<'a> {
}
// If test cannot continue then stop current run and exit test suite.
if !result.can_continue {
return Err(TestCaseError::fail("Test cannot continue."))
return Err(TestCaseError::fail("test cannot continue"))
}

invariant_test.set_last_call_results(result.call_result);
Expand Down
4 changes: 2 additions & 2 deletions crates/evm/evm/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,10 +705,10 @@ pub enum EvmError {
#[error(transparent)]
Abi(#[from] alloy_dyn_abi::Error),
/// Error caused which occurred due to calling the `skip` cheatcode.
#[error("{_0}")]
#[error("{0}")]
Skip(SkipReason),
/// Any other error.
#[error("{}", foundry_common::errors::display_chain(.0))]
#[error("{0}")]
Eyre(
#[from]
#[source]
Expand Down
2 changes: 1 addition & 1 deletion crates/forge/tests/cli/test_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2884,7 +2884,7 @@ contract ForkTest is Test {
cmd.args(["test", "--mt", "test_fork_err_message"]).assert_failure().stdout_eq(str![[r#"
...
Ran 1 test for test/ForkTest.t.sol:ForkTest
[FAIL: vm.createSelectFork: Could not instantiate forked environment with provider eth-mainnet.g.alchemy.com] test_fork_err_message() ([GAS])
[FAIL: vm.createSelectFork: could not instantiate forked environment with provider eth-mainnet.g.alchemy.com; failed to get latest block number; [..]] test_fork_err_message() ([GAS])
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED]
...
Expand Down

0 comments on commit 66e3648

Please sign in to comment.