Skip to content

Commit 649c75c

Browse files
committed
Improve errors.R: hyperlinks + bits
- chain_error() takes a 'srcref' argument, for advanced use and wrappers. - better printing of stack traces with operators, i.e. we do not add pkg:: to an operator. - use 'procsrcrefs' column to avoid the more common 'srcref' name that testthat/rlang use, thinking that it is a proper source ref - add hyperlinks to stack traces, for cli >= 3.4.1.9000
1 parent 53952aa commit 649c75c

1 file changed

Lines changed: 46 additions & 16 deletions

File tree

R/errors.R

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,16 @@
129129
#
130130
# * Major rewrite, use rlang compatible error objects. New API.
131131
#
132-
# ##3 3.0.1 -- 2022-06-17
132+
# ### 3.0.1 -- 2022-06-17
133133
#
134134
# * Remove the `rlang_error` and `rlang_trace` classes, because our new
135135
# deparsed `call` column in the trace is not compatible with rlang.
136+
#
137+
# ### 3.0.2 -- 2022-08-01
138+
#
139+
# * Use a `procsrcref` column for processed source references.
140+
# Otherwise testthat (and probably other rlang based packages), will
141+
# pick up the `srcref` column, and they expect an `srcref` object there.
136142

137143
err <- local({
138144

@@ -303,15 +309,16 @@ err <- local({
303309
#' @param err Error object or message to use for the child error.
304310
#' @param call Call to use in the re-thrown error. See [throw()].
305311

306-
chain_error <- function(expr, err, call = sys.call(-1)) {
312+
chain_error <- function(expr, err, call = sys.call(-1), srcref = NULL) {
307313
.hide_from_trace <- 1
308314
force(call)
309-
srcref <- utils::getSrcref(sys.call())
315+
srcref <- srcref %||% utils::getSrcref(sys.call())
310316
withCallingHandlers({
311317
expr
312318
}, error = function(e) {
313319
.hide_from_trace <- 0:1
314320
e$srcref <- srcref
321+
e$procsrcref <- NULL
315322
if (!inherits(err, "condition")) {
316323
err <- new_error(err, call. = call)
317324
}
@@ -342,6 +349,7 @@ err <- local({
342349
error = function(e) {
343350
.hide_from_trace <- 0:1
344351
e$srcref <- srcref
352+
e$procsrcref <- NULL
345353
e$call <- call
346354
name <- native_name(.NAME)
347355
err <- new_error("Native call to `", name, "` failed", call. = call1)
@@ -376,6 +384,7 @@ err <- local({
376384
error = function(e) {
377385
.hide_from_trace <- 0:1
378386
e$srcref <- srcref
387+
e$procsrcref <- NULL
379388
e$call <- call
380389
name <- native_name(.NAME)
381390
err <- new_error("Native call to `", name, "` failed", call. = call1)
@@ -409,7 +418,11 @@ err <- local({
409418
namespaces <- unlist(lapply(
410419
seq_along(frames),
411420
function(i) {
412-
env_label(topenvx(environment(sys.function(i))))
421+
if (is_operator(calls[[i]])) {
422+
"o"
423+
} else {
424+
env_label(topenvx(environment(sys.function(i))))
425+
}
413426
}
414427
))
415428
pids <- rep(cond$`_pid` %||% Sys.getpid(), length(calls))
@@ -443,6 +456,7 @@ err <- local({
443456
pcs <- lapply(calls, function(c) process_call(list(call = c)))
444457
calls <- lapply(pcs, "[[", "call")
445458
srcrefs <- I(lapply(pcs, "[[", "srcref"))
459+
procsrcrefs <- I(lapply(pcs, "[[", "procsrcref"))
446460

447461
cond$trace <- new_trace(
448462
calls,
@@ -451,12 +465,18 @@ err <- local({
451465
namespaces = namespaces,
452466
scopes = scopes,
453467
srcrefs = srcrefs,
468+
procsrcrefs = procsrcrefs,
454469
pids
455470
)
456471

457472
cond
458473
}
459474

475+
is_operator <- function(cl) {
476+
is.call(cl) && length(cl) >= 1 && is.symbol(cl[[1]]) &&
477+
grepl("^[^.a-zA-Z]", as.character(cl[[1]]))
478+
}
479+
460480
mark_invisible_frames <- function(funs, frames) {
461481
visibles <- rep(TRUE, length(frames))
462482
hide <- lapply(frames, "[[", ".hide_from_trace")
@@ -527,14 +547,15 @@ err <- local({
527547
topenv(x, matchThisEnv = err_env)
528548
}
529549

530-
new_trace <- function (calls, parents, visibles, namespaces, scopes, srcrefs, pids) {
550+
new_trace <- function (calls, parents, visibles, namespaces, scopes, srcrefs, procsrcrefs, pids) {
531551
trace <- data.frame(
532552
stringsAsFactors = FALSE,
533553
parent = parents,
534554
visible = visibles,
535555
namespace = namespaces,
536556
scope = scopes,
537557
srcref = srcrefs,
558+
procsrcref = procsrcrefs,
538559
pid = pids
539560
)
540561
trace$call <- calls
@@ -757,7 +778,7 @@ err <- local({
757778
format_header_line_cli <- function(x, prefix = NULL) {
758779
p_error <- format_error_heading_cli(x, prefix)
759780
p_call <- format_call_cli(x$call)
760-
p_srcref <- format_srcref_cli(conditionCall(x), x$srcref)
781+
p_srcref <- format_srcref_cli(conditionCall(x), x$procsrcref %||% x$srcref)
761782
paste0(p_error, p_call, p_srcref, if (!is.null(conditionCall(x))) ":")
762783
}
763784

@@ -829,10 +850,10 @@ err <- local({
829850
rep(TRUE, nrow(x))
830851
}
831852

832-
srcref <- if ("srcref" %in% names(x)) {
853+
srcref <- if ("srcref" %in% names(x) || "procsrcref" %in% names(x)) {
833854
vapply(
834855
seq_len(nrow(x)),
835-
function(i) format_srcref_cli(x$call[[i]], x$srcref[[i]]),
856+
function(i) format_srcref_cli(x$call[[i]], x$procsrcref[[i]] %||% x$srcref[[i]]),
836857
character(1)
837858
)
838859
} else {
@@ -843,7 +864,9 @@ err <- local({
843864
cli::col_silver(format(x$num), ". "),
844865
ifelse (visible, "", "| "),
845866
scope,
846-
vapply(x$call, format_trace_call_cli, character(1)),
867+
vapply(seq_along(x$call), function(i) {
868+
format_trace_call_cli(x$call[[i]], x$namespace[[i]])
869+
}, character(1)),
847870
srcref
848871
)
849872

@@ -855,10 +878,16 @@ err <- local({
855878
lines
856879
}
857880

858-
format_trace_call_cli <- function(call) {
881+
format_trace_call_cli <- function(call, ns = "") {
882+
envir <- tryCatch(asNamespace(ns), error = function(e) .GlobalEnv)
859883
cl <- trimws(format(call))
860884
if (length(cl) > 1) { cl <- paste0(cl[1], " ", cli::symbol$ellipsis) }
861-
fmc <- cli::code_highlight(cl)[1]
885+
# Older cli does not have 'envir'.
886+
if ("envir" %in% names(formals(cli::code_highlight))) {
887+
fmc <- cli::code_highlight(cl, envir = envir)[1]
888+
} else {
889+
fmc <- cli::code_highlight(cl)[1]
890+
}
862891
cli::ansi_strtrim(fmc, cli::console_width() - 5)
863892
}
864893

@@ -897,10 +926,10 @@ err <- local({
897926
rep(TRUE, nrow(x))
898927
}
899928

900-
srcref <- if ("srcref" %in% names(x)) {
929+
srcref <- if ("srcref" %in% names(x) || "procsrfref" %in% names(x)) {
901930
vapply(
902931
seq_len(nrow(x)),
903-
function(i) format_srcref_plain(x$call[[i]], x$srcref[[i]]),
932+
function(i) format_srcref_plain(x$call[[i]], x$procsrcref[[i]] %||% x$srcref[[i]]),
904933
character(1)
905934
)
906935
} else {
@@ -925,7 +954,7 @@ err <- local({
925954
format_header_line_plain <- function(x, prefix = NULL) {
926955
p_error <- format_error_heading_plain(x, prefix)
927956
p_call <- format_call_plain(x$call)
928-
p_srcref <- format_srcref_plain(conditionCall(x), x$srcref)
957+
p_srcref <- format_srcref_plain(conditionCall(x), x$procsrcref %||% x$srcref)
929958
paste0(p_error, p_call, p_srcref, if (!is.null(conditionCall(x))) ":")
930959
}
931960

@@ -990,15 +1019,16 @@ err <- local({
9901019
}
9911020

9921021
process_call <- function(cond) {
993-
cond[c("call", "srcref")] <- list(
1022+
cond[c("call", "srcref", "procsrcref")] <- list(
9941023
call = if (is.null(cond$call)) {
9951024
NULL
9961025
} else if (is.character(cond$call)) {
9971026
cond$call
9981027
} else {
9991028
deparse(cond$call, nlines = 2)
10001029
},
1001-
srcref = get_srcref(cond$call, cond$srcref)
1030+
srcref = NULL,
1031+
procsrcref = get_srcref(cond$call, cond$procsrcref %||% cond$srcref)
10021032
)
10031033
cond
10041034
}

0 commit comments

Comments
 (0)