Skip to content
This repository was archived by the owner on Aug 16, 2021. It is now read-only.

Only generate backtraces with RUST_BACKTRACE set (closes #7) #27

Merged
merged 1 commit into from
Aug 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ provides a few unique features:
"chain" errors with the `chain_err` method.
* Introducing new errors is trivial. Simple errors can be introduced
at the error site with just a string.
* Errors create and propagate backtraces.
* Errors can create and propagate backtraces.

[Documentation](http://brson.github.io/error-chain/index.html).

Expand Down
50 changes: 31 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! consistent way - `From` conversion behavior is never specified
//! explicitly.
//! * Errors implement Send.
//! * Errors carry backtraces.
//! * Errors can carry backtraces.
//!
//! Similar to other libraries like [error-type] and [quick-error], this
//! library defines a macro, `error_chain!` that declares the types
Expand Down Expand Up @@ -40,9 +40,9 @@
//! errors.
//! * It provides automatic `From` conversions between any other error
//! type that hides the type of the other error in the `cause` box.
//! * It collects a single backtrace at the earliest opportunity and
//! propagates it down the stack through `From` and `ChainErr`
//! conversions.
//! * If `RUST_BACKTRACE` is enabled, it collects a single backtrace at
//! the earliest opportunity and propagates it down the stack through
//! `From` and `ChainErr` conversions.
//!
//! To accomplish its goals it makes some tradeoffs:
//!
Expand Down Expand Up @@ -175,13 +175,13 @@
//! #[derive(Debug)]
//! pub struct Error(pub ErrorKind,
//! pub Option<Box<StdError + Send>>,
//! pub Arc<error_chain::Backtrace>);
//! pub Option<Arc<error_chain::Backtrace>>);
//!
//! impl Error {
//! pub fn kind(&self) -> &ErrorKind { ... }
//! pub fn into_kind(self) -> ErrorKind { ... }
//! pub fn iter(&self) -> error_chain::ErrorChainIter { ... }
//! pub fn backtrace(&self) -> &error_chain::Backtrace { ... }
//! pub fn backtrace(&self) -> Option<&error_chain::Backtrace> { ... }
//! }
//!
//! impl StdError for Error { ... }
Expand Down Expand Up @@ -293,10 +293,11 @@
//!
//! ## Backtraces
//!
//! The earliest non-foreign error to be generated creates a single
//! backtrace, which is passed through all `From` conversions and
//! `chain_err` invocations of compatible types. To read the backtrace
//! just call the `backtrace()` method.
//! If the `RUST_BACKTRACE` environment variable is set to anything
//! but ``0``, the earliest non-foreign error to be generated creates
//! a single backtrace, which is passed through all `From` conversions
//! and `chain_err` invocations of compatible types. To read the
//! backtrace just call the `backtrace()` method.
//!
//! ## Iteration
//!
Expand Down Expand Up @@ -343,7 +344,7 @@ macro_rules! error_chain {
#[derive(Debug)]
pub struct $error_name(pub $error_kind_name,
pub (Option<Box<::std::error::Error + Send>>,
::std::sync::Arc<$crate::Backtrace>));
Option<::std::sync::Arc<$crate::Backtrace>>));

#[allow(unused)]
impl $error_name {
Expand All @@ -359,8 +360,8 @@ macro_rules! error_chain {
$crate::ErrorChainIter(Some(self))
}

pub fn backtrace(&self) -> &$crate::Backtrace {
&(self.1).1
pub fn backtrace(&self) -> Option<&$crate::Backtrace> {
(self.1).1.as_ref().map(|v| &**v)
}
}

Expand Down Expand Up @@ -402,29 +403,29 @@ macro_rules! error_chain {
fn from(e: $foreign_link_error_path) -> Self {
$error_name(
$error_kind_name::$foreign_link_variant(e),
(None, ::std::sync::Arc::new($crate::Backtrace::new())))
(None, $crate::make_backtrace()))
}
}
) *

impl From<$error_kind_name> for $error_name {
fn from(e: $error_kind_name) -> Self {
$error_name(e,
(None, ::std::sync::Arc::new($crate::Backtrace::new())))
(None, $crate::make_backtrace()))
}
}

impl<'a> From<&'a str> for $error_name {
fn from(s: &'a str) -> Self {
$error_name(s.into(),
(None, ::std::sync::Arc::new($crate::Backtrace::new())))
(None, $crate::make_backtrace()))
}
}

impl From<String> for $error_name {
fn from(s: String) -> Self {
$error_name(s.into(),
(None, ::std::sync::Arc::new($crate::Backtrace::new())))
(None, $crate::make_backtrace()))
}
}

Expand Down Expand Up @@ -500,7 +501,7 @@ macro_rules! error_chain {
let e = Box::new(e) as Box<::std::error::Error + Send + 'static>;
let (e, backtrace) = backtrace_from_box(e);
let backtrace = backtrace.unwrap_or_else(
|| ::std::sync::Arc::new($crate::Backtrace::new()));
|| $crate::make_backtrace());

$error_name(callback().into(), (Some(e), backtrace))
})
Expand All @@ -513,7 +514,7 @@ macro_rules! error_chain {
// machinery to make it work.
fn backtrace_from_box(mut e: Box<::std::error::Error + Send + 'static>)
-> (Box<::std::error::Error + Send + 'static>,
Option<::std::sync::Arc<$crate::Backtrace>>) {
Option<Option<::std::sync::Arc<$crate::Backtrace>>>) {
let mut backtrace = None;

e = match e.downcast::<$error_name>() {
Expand Down Expand Up @@ -657,6 +658,7 @@ macro_rules! error_chain {

use std::error::Error as StdError;
use std::iter::Iterator;
use std::sync::Arc;

pub struct ErrorChainIter<'a>(pub Option<&'a StdError>);

Expand All @@ -673,3 +675,13 @@ impl<'a> Iterator for ErrorChainIter<'a> {
}
}
}

/// Returns a backtrace of the current call stack if `RUST_BACKTRACE`
/// is set to anything but ``0``, and `None` otherwise. This is used
/// in the generated error implementations.
pub fn make_backtrace() -> Option<Arc<Backtrace>> {
match std::env::var_os("RUST_BACKTRACE") {
Some(ref val) if val != "0" => Some(Arc::new(Backtrace::new())),
_ => None
}
}
27 changes: 27 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,33 @@ fn empty() {
error_chain! { };
}

#[test]
fn has_backtrace_depending_on_env() {
use std::env;

error_chain! {
types {}
links {}
foreign_links {}
errors {
MyError
}
}

// missing RUST_BACKTRACE and RUST_BACKTRACE=0
env::remove_var("RUST_BACKTRACE");
let err = Error::from(ErrorKind::MyError);
assert!(err.backtrace().is_none());
env::set_var("RUST_BACKTRACE", "0");
let err = Error::from(ErrorKind::MyError);
assert!(err.backtrace().is_none());

// RUST_BACKTRACE set to anything but 0
env::set_var("RUST_BACKTRACE", "yes");
let err = Error::from(ErrorKind::MyError);
assert!(err.backtrace().is_some());
}

#[cfg(test)]
mod foreign_link_test {

Expand Down