Skip to content

Commit

Permalink
Add Report for user-friendly error output
Browse files Browse the repository at this point in the history
  • Loading branch information
shepmaster committed Oct 2, 2022
1 parent c4010db commit 2bb0f98
Show file tree
Hide file tree
Showing 25 changed files with 885 additions and 1 deletion.
24 changes: 24 additions & 0 deletions .cirrus.yml
Expand Up @@ -137,6 +137,14 @@ nightly_test_task:
- cd compatibility-tests/provider-api/
- rustc --version
- cargo test
report_provider_api_test_script:
- cd compatibility-tests/report-provider-api/
- rustc --version
- cargo test
report_try_trait_test_script:
- cd compatibility-tests/report-try-trait/
- rustc --version
- cargo test
before_cache_script: rm -rf $CARGO_HOME/registry/index

unstable_std_backtraces_test_task:
Expand Down Expand Up @@ -188,3 +196,19 @@ v1_34_test_task:
- rustc --version
- cargo test
before_cache_script: rm -rf $CARGO_HOME/registry/index

v1_61_test_task:
name: "Rust 1.61"
container:
image: rust:1.61
cpu: 1
memory: 2Gi
cargo_cache:
folder: $CARGO_HOME/registry
fingerprint_script: cat Cargo.toml
primary_test_script:
- rustup self update
- cd compatibility-tests/v1_61/
- rustc --version
- cargo test
before_cache_script: rm -rf $CARGO_HOME/registry/index
11 changes: 10 additions & 1 deletion Cargo.toml
Expand Up @@ -33,8 +33,14 @@ std = []
# Implement the `core::error::Error` trait.
unstable-core-error = []

# Add support for `async` / `.await`
rust_1_39 = ["snafu-derive/rust_1_39"]

# Add support for `#[track_caller]`
rust_1_46 = ["snafu-derive/rust_1_46"]
rust_1_46 = ["rust_1_39", "snafu-derive/rust_1_46"]

# Add support for `Termination` for `Report`
rust_1_61 = ["rust_1_46", "snafu-derive/rust_1_61"]

# Makes the backtrace type live
backtraces = ["std", "backtrace"]
Expand All @@ -51,6 +57,9 @@ backtraces-impl-std = []
# The std::error::Error provider API will be implemented.
unstable-provider-api = ["snafu-derive/unstable-provider-api"]

# Add support for `FromResidual` for `Report`
unstable-try-trait = []

# The standard library's implementation of futures
futures = ["futures-core-crate", "pin-project"]

Expand Down
14 changes: 14 additions & 0 deletions compatibility-tests/compile-fail/tests/ui/report.rs
@@ -0,0 +1,14 @@
use std::process::ExitCode;

#[snafu::report]
const NOT_HERE: u8 = 42;

#[snafu::report]
fn cannot_add_report_macro_with_no_return_value() {}

#[snafu::report]
fn cannot_add_report_macro_with_non_result_return_value() -> ExitCode {
ExitCode::SUCCESS
}

fn main() {}
23 changes: 23 additions & 0 deletions compatibility-tests/compile-fail/tests/ui/report.stderr
@@ -0,0 +1,23 @@
error: `#[snafu::report]` may only be used on functions
--> tests/ui/report.rs:4:1
|
4 | const NOT_HERE: u8 = 42;
| ^^^^^

error[E0277]: the trait bound `(): __InternalExtractErrorType` is not satisfied
--> tests/ui/report.rs:6:1
|
6 | #[snafu::report]
| ^^^^^^^^^^^^^^^^ the trait `__InternalExtractErrorType` is not implemented for `()`
|
= help: the trait `__InternalExtractErrorType` is implemented for `Result<T, E>`
= note: this error originates in the attribute macro `snafu::report` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `ExitCode: __InternalExtractErrorType` is not satisfied
--> tests/ui/report.rs:9:1
|
9 | #[snafu::report]
| ^^^^^^^^^^^^^^^^ the trait `__InternalExtractErrorType` is not implemented for `ExitCode`
|
= help: the trait `__InternalExtractErrorType` is implemented for `Result<T, E>`
= note: this error originates in the attribute macro `snafu::report` (in Nightly builds, run with -Z macro-backtrace for more info)
9 changes: 9 additions & 0 deletions compatibility-tests/report-provider-api/Cargo.toml
@@ -0,0 +1,9 @@
[package]
name = "report-provider-api"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
snafu = { path = "../..", features = ["backtraces-impl-std", "unstable-provider-api"] }
1 change: 1 addition & 0 deletions compatibility-tests/report-provider-api/rust-toolchain
@@ -0,0 +1 @@
nightly
66 changes: 66 additions & 0 deletions compatibility-tests/report-provider-api/src/lib.rs
@@ -0,0 +1,66 @@
#![cfg(test)]
#![feature(error_generic_member_access, provide_any)]

use snafu::{prelude::*, Report};
use std::process::ExitCode;

#[test]
fn provided_exit_code_is_returned() {
use std::process::Termination;

#[derive(Debug, Snafu)]
enum TwoKindError {
#[snafu(provide(ExitCode => ExitCode::from(2)))]
Mild,
#[snafu(provide(ExitCode => ExitCode::from(3)))]
Extreme,
}

let mild = Report::from_error(MildSnafu.build()).report();
let expected_mild = ExitCode::from(2);

assert!(
nasty_hack_exit_code_eq(mild, expected_mild),
"Wanted {:?} but got {:?}",
expected_mild,
mild,
);

let extreme = Report::from_error(ExtremeSnafu.build()).report();
let expected_extreme = ExitCode::from(3);

assert!(
nasty_hack_exit_code_eq(extreme, expected_extreme),
"Wanted {:?} but got {:?}",
expected_extreme,
extreme,
);
}

#[test]
fn provided_backtrace_is_printed() {
#[derive(Debug, Snafu)]
struct Error {
backtrace: snafu::Backtrace,
}

let r = Report::from_error(Snafu.build());
let msg = r.to_string();

let this_function = "::provided_backtrace_is_printed";
assert!(
msg.contains(this_function),
"Expected {msg:?} to contain {this_function:?}"
);
}

fn nasty_hack_exit_code_eq(left: ExitCode, right: ExitCode) -> bool {
use std::mem;

let (left, right): (u8, u8) = unsafe {
assert_eq!(mem::size_of::<u8>(), mem::size_of::<ExitCode>());
(mem::transmute(left), mem::transmute(right))
};

left == right
}
9 changes: 9 additions & 0 deletions compatibility-tests/report-try-trait/Cargo.toml
@@ -0,0 +1,9 @@
[package]
name = "report-try-trait"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
snafu = { path = "../..", features = ["unstable-try-trait"] }
1 change: 1 addition & 0 deletions compatibility-tests/report-try-trait/rust-toolchain
@@ -0,0 +1 @@
nightly
18 changes: 18 additions & 0 deletions compatibility-tests/report-try-trait/src/lib.rs
@@ -0,0 +1,18 @@
#![cfg(test)]
#![feature(try_trait_v2)]

use snafu::{prelude::*, Report};

#[test]
fn can_be_used_with_the_try_operator() {
#[derive(Debug, Snafu)]
struct ExampleError;

fn mainlike() -> Report<ExampleError> {
ExampleSnafu.fail()?;

Report::ok()
}

let _: Report<ExampleError> = mainlike();
}
13 changes: 13 additions & 0 deletions compatibility-tests/v1_34/src/lib.rs
Expand Up @@ -95,3 +95,16 @@ mod opaque_style {
let _ = create();
}
}

mod report {
use snafu::prelude::*;

#[derive(Debug, Snafu)]
struct Error;

#[test]
#[snafu::report]
fn it_works() -> Result<(), Error> {
Ok(())
}
}
9 changes: 9 additions & 0 deletions compatibility-tests/v1_61/Cargo.toml
@@ -0,0 +1,9 @@
[package]
name = "v1_61"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
snafu = { path = "../..", features = ["rust_1_61"] }
1 change: 1 addition & 0 deletions compatibility-tests/v1_61/rust-toolchain
@@ -0,0 +1 @@
1.61
46 changes: 46 additions & 0 deletions compatibility-tests/v1_61/src/lib.rs
@@ -0,0 +1,46 @@
#![cfg(test)]

use snafu::{prelude::*, Report};
use std::process::ExitCode;

#[test]
fn termination_returns_failure_code() {
use std::process::Termination;

#[derive(Debug, Snafu)]
struct Error;

let r = Report::from_error(Error);
let code: ExitCode = r.report();

assert!(
nasty_hack_exit_code_eq(code, ExitCode::FAILURE),
"Wanted {:?} but got {:?}",
ExitCode::FAILURE,
code,
);
}

#[test]
fn procedural_macro_works_with_result_return_type() {
#[derive(Debug, Snafu)]
struct Error;

#[snafu::report]
fn mainlike_result() -> Result<(), Error> {
Ok(())
}

let _: Report<Error> = mainlike_result();
}

fn nasty_hack_exit_code_eq(left: ExitCode, right: ExitCode) -> bool {
use std::mem;

let (left, right): (u8, u8) = unsafe {
assert_eq!(mem::size_of::<u8>(), mem::size_of::<ExitCode>());
(mem::transmute(left), mem::transmute(right))
};

left == right
}
2 changes: 2 additions & 0 deletions snafu-derive/Cargo.toml
Expand Up @@ -11,7 +11,9 @@ repository = "https://github.com/shepmaster/snafu"
license = "MIT OR Apache-2.0"

[features]
rust_1_39 = []
rust_1_46 = []
rust_1_61 = []
unstable-backtraces-impl-std = []
unstable-provider-api = []

Expand Down
8 changes: 8 additions & 0 deletions snafu-derive/src/lib.rs
Expand Up @@ -19,6 +19,14 @@ pub fn snafu_derive(input: TokenStream) -> TokenStream {
impl_snafu_macro(ast)
}

mod report;
#[proc_macro_attribute]
pub fn report(attr: TokenStream, item: TokenStream) -> TokenStream {
report::body(attr, item)
.unwrap_or_else(|e| e.into_compile_error())
.into()
}

type MultiSynResult<T> = std::result::Result<T, Vec<syn::Error>>;

/// Some arbitrary tokens we treat as a black box
Expand Down

0 comments on commit 2bb0f98

Please sign in to comment.