Skip to content

Commit 837e601

Browse files
clairemcwhiteclauswilke
authored andcommitted
Clearer error messages for invalid aesthetics (#3091)
* Clearer error messages for invalid aesthetics Fixes #3060 * closes #3060 * Modified error messages, fixed spacing, right line endings * Replace use of deparse with rlang::as_label * Fix NEWS mispelling and punctuation. layer.R format suggestions * Left in merge >>>, removed * Remove punctuation in test-layer.R that was failing * Add support for multiple problematic aesthetics, fix NEWS.md addition * Change error to display whole problematic phrase, like fill = density * Update tests to be more specific * Fix failing tests, ideally * Another go at test passing * expect_error uses regex, so ( caused failing. Added fixed = TRUE
1 parent cd6f7ca commit 837e601

File tree

4 files changed

+50
-0
lines changed

4 files changed

+50
-0
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ core developer team.
9191

9292
* ggplot2 now works in Turkish locale (@yutannihilation, #3011).
9393

94+
* Clearer error messages for inappropriate aesthetics (@clairemcwhite, #3060).
95+
9496
* ggplot2 no longer attaches any external packages when using functions that
9597
depend on packages that are suggested but not imported by ggplot2. The
9698
affected functions include `geom_hex()`, `stat_binhex()`,

R/layer.r

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ Layer <- ggproto("Layer", NULL,
242242
evaled <- lapply(aesthetics, eval_tidy, data = data)
243243
evaled <- compact(evaled)
244244

245+
nondata_cols <- check_nondata_cols(evaled)
246+
if (length(nondata_cols) > 0) {
247+
msg <- paste0(
248+
"Aesthetics must be valid data columns. Problematic aesthetic(s): ",
249+
paste0(vapply(nondata_cols, function(x) {paste0(x, " = ", as_label(aesthetics[[x]]))}, character(1)), collapse = ", "),
250+
". \nDid you mistype the name of a data column or forget to add stat()?"
251+
)
252+
stop(msg, call. = FALSE)
253+
}
254+
245255
n <- nrow(data)
246256
if (n == 0) {
247257
# No data, so look at longest evaluated aesthetic
@@ -293,6 +303,18 @@ Layer <- ggproto("Layer", NULL,
293303
env$stat <- stat
294304

295305
stat_data <- new_data_frame(lapply(new, eval_tidy, data, env))
306+
307+
# Check that all columns in aesthetic stats are valid data
308+
nondata_stat_cols <- check_nondata_cols(stat_data)
309+
if (length(nondata_stat_cols) > 0) {
310+
msg <- paste0(
311+
"Aesthetics must be valid computed stats. Problematic aesthetic(s): ",
312+
paste0(vapply(nondata_stat_cols, function(x) {paste0(x, " = ", as_label(aesthetics[[x]]))}, character(1)), collapse = ", "),
313+
". \nDid you map your stat in the wrong layer?"
314+
)
315+
stop(msg, call. = FALSE)
316+
}
317+
296318
names(stat_data) <- names(new)
297319

298320
# Add any new scales, if needed

R/utilities.r

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@ is.discrete <- function(x) {
350350
is.factor(x) || is.character(x) || is.logical(x)
351351
}
352352

353+
# This function checks that all columns of a dataframe `x` are data and
354+
# returns the names of any columns that are not.
355+
# We define "data" as atomic types or lists, not functions or otherwise
356+
check_nondata_cols <- function(x) {
357+
idx <- (vapply(x, function(x) rlang::is_vector(x), logical(1)))
358+
names(x)[which(!idx)]
359+
}
360+
353361
compact <- function(x) {
354362
null <- vapply(x, is.null, logical(1))
355363
x[!null]

tests/testthat/test-layer.r

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,24 @@ test_that("missing aesthetics trigger informative error", {
3838
)
3939
})
4040

41+
test_that("function aesthetics are wrapped with stat()", {
42+
df <- data_frame(x = 1:10)
43+
expect_error(
44+
ggplot_build(ggplot(df, aes(colour = density, fill = density)) + geom_point()),
45+
"Aesthetics must be valid data columns. Problematic aesthetic(s): colour = density, fill = density",
46+
fixed = TRUE
47+
)
48+
})
49+
50+
test_that("computed stats are in appropriate layer", {
51+
df <- data_frame(x = 1:10)
52+
expect_error(
53+
ggplot_build(ggplot(df, aes(colour = stat(density), fill = stat(density))) + geom_point()),
54+
"Aesthetics must be valid computed stats. Problematic aesthetic(s): colour = stat(density), fill = stat(density)",
55+
fixed = TRUE
56+
)
57+
})
58+
4159
test_that("if an aes is mapped to a function that returns NULL, it is removed", {
4260
df <- data_frame(x = 1:10)
4361
null <- function(...) NULL

0 commit comments

Comments
 (0)