Skip to content

Commit dfd3846

Browse files
authored
Add a link to parse failure location to load_all() (#282)
Co-authored-by: Lionel Henry <lionel.hry@proton.me> * Handle parse errors more accurately
1 parent 9b4f101 commit dfd3846

File tree

3 files changed

+46
-13
lines changed

3 files changed

+46
-13
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# pkgload (development version)
22

3+
* `load_all()` now includes a link to the exact location when loading failed (@olivroy, #282).
4+
35
* User onload hooks are now passed a library path.
46

57
* Fixed an error when updating packages on load (@olivroy, #261).

R/source.R

+43-12
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@ source_many <- function(files, encoding = "UTF-8", envir = parent.frame()) {
1111
for (file in files) {
1212
try_fetch(
1313
source_one(file, encoding, envir = envir),
14-
error = function(cnd) {
15-
path <- file.path(basename(dirname(file)), basename(file))
16-
msg <- paste0("Failed to load {.file {path}}")
17-
cli::cli_abort(msg, parent = cnd, call = quote(load_all()))
18-
}
14+
error = function(cnd) handle_source_error(cnd, file)
1915
)
2016
}
17+
2118
invisible()
2219
}
2320

@@ -26,15 +23,49 @@ source_one <- function(file, encoding, envir = parent.frame()) {
2623
stopifnot(is.environment(envir))
2724

2825
lines <- read_lines_enc(file, file_encoding = encoding)
29-
srcfile <- srcfilecopy(file, lines, file.info(file)[1, "mtime"],
30-
isFile = TRUE)
31-
exprs <- parse(text = lines, n = -1, srcfile = srcfile)
26+
srcfile <- srcfilecopy(file, lines, file.info(file)[1, "mtime"], isFile = TRUE)
3227

33-
n <- length(exprs)
34-
if (n == 0L) return(invisible())
28+
withCallingHandlers(
29+
exprs <- parse(text = lines, n = -1, srcfile = srcfile),
30+
error = function(cnd) handle_parse_error(cnd, file)
31+
)
3532

36-
for (i in seq_len(n)) {
37-
eval(exprs[i], envir)
33+
for (expr in exprs) {
34+
eval(expr, envir)
3835
}
36+
3937
invisible()
4038
}
39+
40+
handle_source_error <- function(cnd, file) {
41+
path <- file.path(basename(dirname(file)), basename(file))
42+
msg <- paste0("Failed to load {.file {path}}")
43+
cli::cli_abort(msg, parent = cnd, call = quote(load_all()))
44+
}
45+
46+
handle_parse_error <- function(cnd, file) {
47+
path <- file.path(basename(dirname(file)), basename(file))
48+
49+
# Tweak base message to be shorter and add link to src location.
50+
msg <- conditionMessage(cnd)
51+
52+
# Extract :<line>:<col> in base message.
53+
location <- regmatches(msg, m = regexpr("\\:\\d+\\:\\d+", msg))
54+
55+
if (length(location) == 0) {
56+
return(zap())
57+
}
58+
59+
suffixed_path <- paste0(path, location)
60+
61+
# Tweak parse() message to include an hyperlink.
62+
# Replace full path by relative path + hyperlink
63+
path_hyperlink <- cli::format_inline(paste0("At {.file ", suffixed_path, "}:"))
64+
msg <- sub(
65+
paste0("^.*", suffixed_path, "\\:"),
66+
path_hyperlink,
67+
msg
68+
)
69+
70+
abort(msg, call = conditionCall(cnd))
71+
}

tests/testthat/_snaps/source.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Error in `load_all()`:
77
! Failed to load 'testSource/b.R'
88
Caused by error in `parse()`:
9-
! testSource/b.R:2:0: unexpected end of input
9+
! At 'testSource/b.R:2:0': unexpected end of input
1010
1: b <-
1111
^
1212

0 commit comments

Comments
 (0)