Skip to content

Macro runtime error not helpful 🐛 #114055

Open
@0nyr

Description

@0nyr

Code

// Here is a basic example of macro that has a runtime error. 

macro_rules! runtime_error_macro {
    () => {
        let test: Option<i32> = None;
        test.unwrap();
    };
}

fn main() {
    runtime_error_macro!();
}

Current output

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:9:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

# with RUST_BACKTRACE=1:
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:9:5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/std/src/panicking.rs:617:5
   1: core::panicking::panic_fmt
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/panicking.rs:67:14
   2: core::panicking::panic
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/panicking.rs:117:5
   3: core::option::Option<T>::unwrap
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/option.rs:935:21
   4: rust_macro_error_example::main
             at ./src/main.rs:9:5
   5: core::ops::function::FnOnce::call_once
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/ops/function.rs:250:5

Desired output

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:9:5
in macro test_failing_macro, src/main.rs:4:12
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

# with RUST_BACKTRACE=1:
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:9:5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/std/src/panicking.rs:617:5
   1: core::panicking::panic_fmt
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/panicking.rs:67:14
   2: core::panicking::panic
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/panicking.rs:117:5
   3: core::option::Option<T>::unwrap
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/option.rs:935:21
   4': rust_macro_error_example::main
             at src/main.rs:4:12
   4: rust_macro_error_example::main
             at ./src/main.rs:9:5
   5: core::ops::function::FnOnce::call_once
             at /rustc/31395ec38250b60b380fd3c27e94756aba3557de/library/core/src/ops/function.rs:250:5

Rationale and extra context

Working with macro, and especially debugging macro code is challenging in Rust. Contrary to all the helpful Rust compile and runtime errors we are used to for non-macro code, error line is not given if any runtime error occurs inside a macro expansion. This quickly becomes challenging, with macros within macros.

It seems like related Issues are also related to this issue, like #100235.

In a related closed discussion about improving compile-time macro errors it is recalled that there are actually 2 users of macros: people that develop them, and people that use them.

In the context of runtime error, it seems logical to give both the line invocation of the macro, as well as the line inside the macro expansion that gives the error, so that both macro users can better report bugs and errors, and developers can easily correct the problem. This information should at least be present in the backtrack of the error (while using RUST_BACKTRACE=1).

Currently, there isn't an automatic way to have Rust provide line numbers within the expanded macros when you encounter a runtime error. Since macro expansion is a compile-time operation, and it abstracts away the internals of the macro from the runtime system, it's seems hard to develop such a system. It's only possible to expand macros using a nightly compile flag of rustc called -Zunpretty=expanded, but it's very insufficient, as it just prints a new expanded flag, with no automatic compile or run, making debugging of runtime errors in macro challenging. The proposed improvement would really improve greatly this process, as well a bug reporting.

Other cases

Although the runtime error gives the macro invocation line, as well as the code error origin, here 'Option::unwrap()' on a 'None' value", this is not really helpful. In a real project scenario, with dozens of unwrap and nested macros, it's quite challenging to rely only on the knowledge that the error is occurring on an unwrap. I took the example of unwrap but it's the same for any runtime error inside a macro.

Below is a slightly more complex example:

macro_rules! runtime_error_macro {
    () => {
        let test: Option<i32> = None;
        test.unwrap();
    };
}

macro_rules! runtime_error_macro_nested {
    () => {
        let test1: Option<i32> = None;
        test1.unwrap();

        let test2: Option<i32> = Some(42);
        test2.unwrap();

        runtime_error_macro!();
    };
}

fn main() {
    println!("Hello, world!");

    runtime_error_macro_nested!();
}

Compiling and running with either cargo run or rustc src/main.rs -o main, ./main gives:

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:23:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

In this situation, it's easy to understand that having more information of the line of the error from within the error invocation, or information about the context, like lines before and after, would be very previous to understand where the error is actually coming from.

Anything else?

This issue has been created by fitz35 and me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-debuginfoArea: Debugging information in compiled programs (DWARF, PDB, etc.)A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions