Skip to content

Commit f6e8827

Browse files
committed
docs: Show error source for translation errors
1 parent 6e976c5 commit f6e8827

File tree

7 files changed

+77
-38
lines changed

7 files changed

+77
-38
lines changed

R/handle_desc.R

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
# Handles calls to 'desc' function by
33
# - extracting the sort order
44
# - removing any desc-function calls from the expressions: desc(colname) -> colname
5-
handle_desc <- function(dots) {
5+
handle_desc <- function(dots, call = caller_env()) {
66
ascending <- rep(TRUE, length(dots))
77

88
for (i in seq_along(dots)) {
99
expr <- quo_get_expr(dots[[i]])
1010
env <- quo_get_env(dots[[i]])
1111

12-
if (is_desc(expr, env)) {
12+
if (is_desc(expr, env, call)) {
1313
ascending[[i]] <- FALSE
1414
dots[[i]] <- new_quosure(expr[[2]], env = env)
1515
}
@@ -18,13 +18,14 @@ handle_desc <- function(dots) {
1818
list(dots = dots, ascending = ascending)
1919
}
2020

21-
is_desc <- function(expr, env) {
21+
is_desc <- function(expr, env, call) {
2222
if (!is.call(expr)) {
2323
return(FALSE)
2424
}
2525

2626
if (expr[[1]] == "desc") {
2727
if (!identical(eval(expr[[1]], env), dplyr::desc)) {
28+
# Error handled elsewhere
2829
return(FALSE)
2930
}
3031
} else if (expr[[1]][[1]] == "::") {
@@ -36,7 +37,7 @@ is_desc <- function(expr, env) {
3637
}
3738

3839
if (length(expr) > 2) {
39-
cli::cli_abort("{.fun desc} must be called with exactly one argument.")
40+
cli::cli_abort("Function {.fun desc} must be called with exactly one argument.", call = call)
4041
}
4142

4243
TRUE

R/relational.R

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@ rel_try <- function(call, rel, ...) {
7878
cli::cli_abort("Must use a {.code return()} in {.code rel_try()}.", .internal = TRUE)
7979
}
8080

81-
rel_translate_dots <- function(dots, data) {
81+
rel_translate_dots <- function(dots, data, call = caller_env()) {
8282
if (is.null(names(dots))) {
83-
map(dots, rel_translate, data)
83+
map(dots, rel_translate, data, call = call)
8484
} else {
85-
imap(dots, rel_translate, data = data)
85+
imap(dots, rel_translate, data = data, call = call)
8686
}
8787
}
8888

8989
# Currently does not support referring to names created during the `summarise()` call.
9090
# Also has specific support for `across()`.
91-
rel_translate_dots_summarise <- function(dots, data) {
91+
rel_translate_dots_summarise <- function(dots, data, call = caller_env()) {
9292
stopifnot(
9393
!is.null(names(dots))
9494
)
@@ -101,10 +101,22 @@ rel_translate_dots_summarise <- function(dots, data) {
101101

102102
if (is.null(expanded)) {
103103
new <- names(dots)[[.y]]
104-
translation <- list(rel_translate(dots[[.y]], alias = new, data, names_forbidden = .x$new))
104+
translation <- list(rel_translate(
105+
dots[[.y]],
106+
alias = new,
107+
data,
108+
names_forbidden = .x$new,
109+
call = call
110+
))
105111
} else {
106112
new <- names(expanded)
107-
translation <- imap(expanded, function(expr, name) rel_translate(expr, alias = name, data, names_forbidden = .x$new))
113+
translation <- imap(expanded, function(expr, name) rel_translate(
114+
expr,
115+
alias = name,
116+
data,
117+
names_forbidden = .x$new,
118+
call = call
119+
))
108120
}
109121

110122
list(

R/translate.R

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Documented in `.github/CONTRIBUTING.md`
22

3-
rel_find_call <- function(fun, env) {
3+
rel_find_call <- function(fun, env, call = caller_env()) {
44
name <- as.character(fun)
55

66
if (name[[1]] == "::") {
@@ -106,7 +106,7 @@ rel_find_call <- function(fun, env) {
106106
# Remember to update limits.Rmd when adding new functions!
107107

108108
if (is.null(pkgs)) {
109-
cli::cli_abort("No translation for function {.code {name}}.")
109+
cli::cli_abort("No translation for function {.fun {name}}.")
110110
}
111111

112112
# https://github.com/tidyverse/dplyr/pull/7046
@@ -123,9 +123,9 @@ rel_find_call <- function(fun, env) {
123123
}
124124

125125
if (length(pkgs) == 1) {
126-
cli::cli_abort("Function {.code {name}} does not map to {.code {pkgs}::{name}}.")
126+
cli::cli_abort("Function {.fun {name}} does not map to {.fun {pkgs}::{name}}.", call = call)
127127
} else {
128-
cli::cli_abort("Function {.code {name}} does not map to the corresponding function in {.pkg {pkgs}}.")
128+
cli::cli_abort("Function {.fun {name}} does not map to the corresponding function in {.pkg {pkgs}}.", call = call)
129129
}
130130
}
131131

@@ -158,15 +158,18 @@ rel_translate_lang <- function(
158158
# FIXME: Perform constant folding instead
159159
partition,
160160
in_window,
161-
need_window
161+
need_window,
162+
call = caller_env()
162163
) {
163-
pkg_name <- rel_find_call(expr[[1]], env)
164+
pkg_name <- rel_find_call(expr[[1]], env, call = call)
164165
pkg <- pkg_name[[1]]
165166
name <- pkg_name[[2]]
166167

167168

168169
if (name %in% c(">", "<", "==", ">=", "<=")) {
169-
if (length(expr) != 3) cli::cli_abort("Expected three expressions for comparison. Got {length(expr)}")
170+
if (length(expr) != 3) {
171+
cli::cli_abort("Expected three expressions for comparison. Got {length(expr)}", call = call)
172+
}
170173

171174
class_left <- infer_class_of_expr(expr[[2]], data)
172175
class_right <- infer_class_of_expr(expr[[3]], data)
@@ -185,7 +188,7 @@ rel_translate_lang <- function(
185188
if (!(name %in% c("wday", "strftime", "lag", "lead"))) {
186189
if (!is.null(names(expr)) && any(names(expr) != "")) {
187190
# Fix grepl() logic below when allowing matching by argument name
188-
cli::cli_abort("Can't translate named argument {.code {name}({names(expr)[names(expr) != ''][[1]]} = )}.")
191+
cli::cli_abort("Can't translate named argument {.code {name}({names(expr)[names(expr) != ''][[1]]} = )}.", call = call)
189192
}
190193
}
191194

@@ -196,17 +199,17 @@ rel_translate_lang <- function(
196199
# Hack
197200
"wday" = {
198201
if (!is.null(pkg) && pkg != "lubridate") {
199-
cli::cli_abort("Don't know how to translate {.code {pkg}::{name}}.")
202+
cli::cli_abort("Don't know how to translate {.code {pkg}::{name}}.", call = call)
200203
}
201204
def <- lubridate::wday
202205
call <- match.call(def, expr, envir = env)
203206
args <- as.list(call[-1])
204207
bad <- !(names(args) %in% c("x"))
205208
if (any(bad)) {
206-
cli::cli_abort("{name}({names(args)[which(bad)[[1]]]} = ) not supported")
209+
cli::cli_abort("{name}({names(args)[which(bad)[[1]]]} = ) not supported", call = call)
207210
}
208211
if (!is.null(getOption("lubridate.week.start"))) {
209-
cli::cli_abort('{.code wday()} with {.code option("lubridate.week.start")} not supported')
212+
cli::cli_abort('{.code wday()} with {.code option("lubridate.week.start")} not supported', call = call)
210213
}
211214
},
212215
"strftime" = {
@@ -215,7 +218,7 @@ rel_translate_lang <- function(
215218
args <- as.list(call[-1])
216219
bad <- !(names(args) %in% c("x", "format"))
217220
if (any(bad)) {
218-
cli::cli_abort("{name}({names(args)[which(bad)[[1]]]} = ) not supported")
221+
cli::cli_abort("{name}({names(args)[which(bad)[[1]]]} = ) not supported", call = call)
219222
}
220223
},
221224
"%in%" = {
@@ -237,7 +240,7 @@ rel_translate_lang <- function(
237240
}
238241

239242
if (length(values) > 100) {
240-
cli::cli_abort("Can't translate {.code {name}} with more than 100 values.")
243+
cli::cli_abort("Can't translate {.code {name}} with more than 100 values.", call = call)
241244
}
242245

243246
consts <- map(values, do_translate)
@@ -258,7 +261,7 @@ rel_translate_lang <- function(
258261
if (exists(var_name, envir = env)) {
259262
return(do_translate(get(var_name, env), in_window = in_window))
260263
} else {
261-
cli::cli_abort("internal: object not found, should also be triggered by the dplyr fallback")
264+
cli::cli_abort("object not found, should also be triggered by the dplyr fallback", call = call)
262265
}
263266
}
264267
}
@@ -329,7 +332,7 @@ rel_translate_lang <- function(
329332

330333
if (name == "grepl") {
331334
if (!inherits(args[[1]], "relational_relexpr_constant")) {
332-
cli::cli_abort("Only constant patterns are supported in {.code grepl()}")
335+
cli::cli_abort("Only constant patterns are supported in {.code grepl()}", call = call)
333336
}
334337
}
335338

@@ -353,11 +356,14 @@ rel_translate_lang <- function(
353356
}
354357

355358
rel_translate <- function(
356-
quo, data,
357-
alias = NULL,
358-
partition = NULL,
359-
need_window = FALSE,
360-
names_forbidden = NULL) {
359+
quo,
360+
data,
361+
alias = NULL,
362+
partition = NULL,
363+
need_window = FALSE,
364+
names_forbidden = NULL,
365+
call = caller_env()
366+
) {
361367
if (is_expression(quo)) {
362368
expr <- quo
363369
env <- baseenv()
@@ -378,7 +384,7 @@ rel_translate <- function(
378384
#
379385
symbol = {
380386
if (as.character(expr) %in% names_forbidden) {
381-
cli::cli_abort("Can't reuse summary variable {.var {as.character(expr)}}.")
387+
cli::cli_abort("Can't reuse summary variable {.var {as.character(expr)}}.", call = call)
382388
}
383389
if (as.character(expr) %in% names(data)) {
384390
ref <- as.character(expr)
@@ -399,10 +405,11 @@ rel_translate <- function(
399405
env,
400406
partition,
401407
in_window,
402-
need_window
408+
need_window,
409+
call = call
403410
),
404411
#
405-
cli::cli_abort("Internal: Unknown type {.val {typeof(expr)}}")
412+
cli::cli_abort("Internal: Unknown type {.val {typeof(expr)}}", call = call)
406413
)
407414
}
408415

tests/testthat/_snaps/fallback.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
duckdb_tibble(a = 1, b = 2) %>% mutate(c = .env$x)
116116
Message
117117
i dplyr fallback recorded
118-
{"version":"0.3.1","message":"internal: object not found, should also be triggered by the dplyr fallback","name":"mutate","x":{"...1":"numeric","...2":"numeric"},"args":{"dots":{"...3":"...4$...5"},".by":"NULL",".keep":["all","used","unused","none"]}}
118+
{"version":"0.3.1","message":"object not found, should also be triggered by the dplyr fallback","name":"mutate","x":{"...1":"numeric","...2":"numeric"},"args":{"dots":{"...3":"...4$...5"},".by":"NULL",".keep":["all","used","unused","none"]}}
119119
Condition
120120
Error in `mutate()`:
121121
i In argument: `c = .env$x`.
@@ -128,7 +128,7 @@
128128
duckdb_tibble(a = 1, b = 2) %>% mutate(c = foo(a, b))
129129
Message
130130
i dplyr fallback recorded
131-
{"version":"0.3.1","message":"No translation for function `foo`.","name":"mutate","x":{"...1":"numeric","...2":"numeric"},"args":{"dots":{"...3":"foo(...1, ...2)"},".by":"NULL",".keep":["all","used","unused","none"]}}
131+
{"version":"0.3.1","message":"No translation for function `foo()`.","name":"mutate","x":{"...1":"numeric","...2":"numeric"},"args":{"dots":{"...3":"foo(...1, ...2)"},".by":"NULL",".keep":["all","used","unused","none"]}}
132132
Output
133133
# A duckplyr data frame: 3 variables
134134
a b c

tests/testthat/_snaps/handle_desc.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77
! This operation cannot be carried out by DuckDB, and the input is a frugal duckplyr frame.
88
i Use `compute(prudence = "lavish")` to materialize to temporary storage and continue with duckplyr.
99
i See `vignette("prudence")` for other options.
10-
Caused by error in `rel_find_call()`:
11-
! Function `desc` does not map to `dplyr::desc`.
10+
Caused by error in `arrange()`:
11+
! Function `desc()` does not map to `dplyr::desc()`.
12+
13+
# desc() fails for more than one argument
14+
15+
Code
16+
duckdb_tibble(a = 1:3, b = 4:6, .prudence = "frugal") %>% arrange(desc(a, b))
17+
Condition
18+
Error in `arrange()`:
19+
! This operation cannot be carried out by DuckDB, and the input is a frugal duckplyr frame.
20+
i Use `compute(prudence = "lavish")` to materialize to temporary storage and continue with duckplyr.
21+
i See `vignette("prudence")` for other options.
22+
Caused by error in `arrange()`:
23+
! Function `desc()` must be called with exactly one argument.
1224

tests/testthat/_snaps/translate.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
rel_translate(quo(c(1, b = 2)))
55
Condition
66
Error in `rel_find_call()`:
7-
! No translation for function `c`.
7+
! No translation for function `c()`.
88

99
# a %in% b
1010

tests/testthat/test-handle_desc.R

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,10 @@ test_that("desc() fails if it points elsewhere", {
2424
arrange(desc(a))
2525
})
2626
})
27+
28+
test_that("desc() fails for more than one argument", {
29+
expect_snapshot(error = TRUE, {
30+
duckdb_tibble(a = 1:3, b = 4:6, .prudence = "frugal") %>%
31+
arrange(desc(a, b))
32+
})
33+
})

0 commit comments

Comments
 (0)