hadley / adv-r

Advanced R: a book
http://adv-r.hadley.nz
Other
2.35k stars 1.71k forks source link

rlang::last_trace() example in section 22.3.1 fails due to use of rlang::with_abort() #1740

Open maxwellgreen opened 1 year ago

maxwellgreen commented 1 year ago

In section 22.3.1 ("Lazy evaluation") of chapter 22 ("Debugging"), there is an example intended to demonstrate the rlang traceback, but no traceback appears. I think this example fails because the code uses rlang::with_abort() to generate the error to trace back. with_abort() seems to no longer be a function in rlang (as of version 1.0.5 - the book uses 0.4.10), though I can't find in the changelog when it was removed or deprecated.

The faulty section is as follows:

rlang::with_abort(f(j()))

> Error: Oops!

rlang::last_trace()

> Error: Can't show last error because no error was recorded yet

batpigandme commented 1 year ago

Just adding the link to the referenced section of the Debugging chapter where this example can be found: https://github.com/hadley/adv-r/blob/dc49c3872c3530ac08716fd4f4c235b01266a4ce/Debugging.Rmd#L438-L448

I tried to create something equivalent (and failed) by using rlang::global_handle() as suggested in https://github.com/r-lib/rlang/issues/1351

Here's my attempt (n.b. must be run interactively to work)

rlang::global_handle()
f <- function() g()
g <- function() message("Hi!")
f()
#> Hi!
withCallingHandlers(
  f()
)
#> Hi!
rlang::last_messages()
#> [[1]]
#> <message/rlang_message>
#>   Message in `message()`:
#>   Hi!
#>   ---
#>   Backtrace:
#>   1. base::withCallingHandlers(f())
#>   2. global f()
#>   3. global g()

Since it's a message and not a warning, the promotion to error by way of options(warn = 2) doesn't do anything either.

batpigandme commented 1 year ago

Other approach to above by modifying the warning2error() function from 8.6.3 Resignal: https://github.com/hadley/adv-r/blob/dc49c3872c3530ac08716fd4f4c235b01266a4ce/Conditions.Rmd#L940-L954

library(rlang)
message2error <- function(expr) {
  withCallingHandlers(
    message = function(cnd) abort(conditionMessage(cnd)),
    expr
  )
}

message2error({
  x <- 2 ^ 4
  message("Hello")
})
#> Error:
#>   ! Hello
#> Run `rlang::last_trace()` to see where the error occurred.
rlang::last_trace()
#> <error/rlang_error>
#>   Error:
#>   ! Hello
#> ---
#>   Backtrace:
#>   ▆
#> 1. ├─global message2error(...)
#> 2. │ └─base::withCallingHandlers(...)
#> 3. ├─base::message("Hello")
#> 4. │ ├─base::withRestarts(...)
#> 5. │ │ └─base (local) withOneRestart(expr, restarts[[1L]])
#> 6. │ │   └─base (local) doWithOneRestart(return(expr), restart)
#> 7. │ └─base::signalCondition(cond)
#> 8. └─`<fn>`(`<smplMssg>`)
#> Run rlang::last_trace(drop = FALSE) to see 1 hidden frame.