Skip to content

Commit 49973d4

Browse files
committed
implement changes from review: better organized tests, improved argument documentation in aes(), improved the name of the warning functions, improved function detecting whether or not an expression refers to the plot data
1 parent be3ec01 commit 49973d4

File tree

4 files changed

+58
-47
lines changed

4 files changed

+58
-47
lines changed

R/aes.r

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ NULL
88
#' [ggplot2()] and in individual layers.
99
#'
1010
#' This function also standardises aesthetic names by converting `color` to `colour`
11-
#' (also in substrings, e.g. `point_color` to `point_colour`) and translating old style
12-
#' R names to ggplot names (eg. `pch` to `shape`, `cex` to `size`).
11+
#' (also in substrings, e.g., `point_color` to `point_colour`) and translating old style
12+
#' R names to ggplot names (e.g., `pch` to `shape` and `cex` to `size`).
1313
#'
1414
#' @section Quasiquotation:
1515
#'
@@ -22,9 +22,10 @@ NULL
2222
#' programming vignette](http://dplyr.tidyverse.org/articles/programming.html)
2323
#' to learn more about these techniques.
2424
#'
25-
#' @param x,y,... List of name value pairs giving aesthetics to map to
26-
#' variables. The names for x and y aesthetics are typically omitted because
27-
#' they are so common; all other aesthetics must be named.
25+
#' @param x,y,... List of name-value pairs in the form `aesthetic = column_name`
26+
#' describing which variables in the layer data should be mapped to which
27+
#' aesthetics used by paired geom/stat. The names for x and y aesthetics are typically
28+
#' omitted because they are so common; all other aesthetics must be named.
2829
#' @seealso [vars()] for another quoting function designed for
2930
#' faceting specifications.
3031
#' @return A list with class `uneval`. Components of the list are either
@@ -344,30 +345,28 @@ mapped_aesthetics <- function(x) {
344345
#' @param data The data to be mapped from
345346
#'
346347
#' @noRd
347-
check_aes_extract_usage <- function(mapping, data) {
348-
lapply(mapping, check_aes_extract_usage_quo, data)
349-
}
350-
351-
check_aes_extract_usage_quo <- function(quosure, data) {
352-
check_aes_extract_usage_expr(get_expr(quosure), data, get_env(quosure))
348+
warn_for_aes_extract_usage <- function(mapping, data) {
349+
lapply(mapping, function(quosure) {
350+
warn_for_aes_extract_usage_expr(get_expr(quosure), data, get_env(quosure))
351+
})
353352
}
354353

355-
check_aes_extract_usage_expr <- function(x, data, env = emptyenv()) {
354+
warn_for_aes_extract_usage_expr <- function(x, data, env = emptyenv()) {
356355
if (is_call(x, "[[") || is_call(x, "$")) {
357-
if (extract_target_is_data(x, data, env)) {
358-
good_usage <- check_aes_get_alternative_usage(x)
356+
if (extract_target_is_likely_data(x, data, env)) {
357+
good_usage <- alternative_aes_extract_usage(x)
359358
warning(
360359
"Use of `", format(x), "` is discouraged. ",
361360
"Use `", good_usage, "` instead.",
362361
call. = FALSE
363362
)
364363
}
365364
} else if (is.call(x)) {
366-
lapply(x, check_aes_extract_usage_expr, data, env)
365+
lapply(x, warn_for_aes_extract_usage_expr, data, env)
367366
}
368367
}
369368

370-
check_aes_get_alternative_usage <- function(x) {
369+
alternative_aes_extract_usage <- function(x) {
371370
if (is_call(x, "[[")) {
372371
good_call <- call2("[[", quote(.data), x[[3]])
373372
format(good_call)
@@ -378,7 +377,13 @@ check_aes_get_alternative_usage <- function(x) {
378377
}
379378
}
380379

381-
extract_target_is_data <- function(x, data, env) {
382-
data_eval <- try(eval_tidy(x[[2]], data, env), silent = TRUE)
383-
identical(data_eval, data)
380+
extract_target_is_likely_data <- function(x, data, env) {
381+
if(!is.name(x[[2]])) {
382+
return(FALSE)
383+
}
384+
385+
tryCatch({
386+
data_eval <- eval_tidy(x[[2]], data, env)
387+
identical(data_eval, data)
388+
}, error = function(err) FALSE)
384389
}

R/layer.r

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ Layer <- ggproto("Layer", NULL,
243243
evaled <- compact(evaled)
244244

245245
# Check for discouraged usage in mapping
246-
check_aes_extract_usage(aesthetics, data[setdiff(names(data), "PANEL")])
246+
warn_for_aes_extract_usage(aesthetics, data[setdiff(names(data), "PANEL")])
247247

248248
# Check aesthetic values
249249
nondata_cols <- check_nondata_cols(evaled)

man/aes.Rd

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-aes.r

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -111,39 +111,44 @@ test_that("aes standardises aesthetic names", {
111111
expect_warning(aes(color = x, colour = y), "Duplicated aesthetics")
112112
})
113113

114-
test_that("Improper use of $ and [[ is detected by check_aes_extract_usage()", {
114+
test_that("warn_for_aes_extract_usage() warns for discouraged uses of $ and [[ within aes()", {
115115

116-
returns_x <- function() "x"
117116
df <- data_frame(x = 1:5, nested_df = data_frame(x = 6:10))
118117

119-
# valid extraction in aes()
120-
expect_silent(check_aes_extract_usage(aes(x), df))
121-
expect_silent(check_aes_extract_usage(aes(.data$x), df))
122-
expect_silent(check_aes_extract_usage(aes(.data[["x"]]), df))
123-
expect_silent(check_aes_extract_usage(aes(.data[[!!quo("x")]]), df))
124-
expect_silent(check_aes_extract_usage(aes(.data[[returns_x()]]), df))
125-
expect_silent(check_aes_extract_usage(aes(!!sym("x")), df))
126-
expect_silent(check_aes_extract_usage(aes(x * 10), df))
127-
expect_silent(check_aes_extract_usage(aes(nested_df$x), df))
128-
expect_silent(check_aes_extract_usage(aes(nested_df[["x"]]), df))
129-
expect_silent(check_aes_extract_usage(aes(.data[[c("nested_df", "x")]]), df))
130-
expect_silent(check_aes_extract_usage(aes(.data[[c(2, 1)]]), df))
131-
expect_silent(check_aes_extract_usage(aes(.data[[1]]), df))
132-
133-
# bad: use of extraction
134118
expect_warning(
135-
check_aes_extract_usage(aes(df$x), df),
119+
warn_for_aes_extract_usage(aes(df$x), df),
136120
"Use of `df\\$x` is discouraged"
137121
)
122+
138123
expect_warning(
139-
check_aes_extract_usage(aes(df[["x"]]), df),
124+
warn_for_aes_extract_usage(aes(df[["x"]]), df),
140125
'Use of `df\\[\\["x"\\]\\]` is discouraged'
141126
)
142127
})
143128

144-
test_that("Warnings are issued for extract usage in plots", {
145-
df <- data_frame(x = 1:3, y = 3:1)
146-
p <- ggplot(df, aes(df$x, df$y)) + geom_point()
129+
test_that("warn_for_aes_extract_usage() does not evaluate function calls", {
130+
df <- data_frame(x = 1:5, nested_df = data_frame(x = 6:10))
131+
returns_df <- function() df
132+
133+
expect_warning(warn_for_aes_extract_usage(aes(df$x), df))
134+
expect_silent(warn_for_aes_extract_usage(aes(returns_df()$x), df))
135+
})
136+
137+
test_that("warn_for_aes_extract_usage() does not warn for valid uses of $ and [[ within aes()", {
138+
df <- data_frame(x = 1:5, nested_df = data_frame(x = 6:10))
139+
140+
# use of .data
141+
expect_silent(warn_for_aes_extract_usage(aes(.data$x), df))
142+
expect_silent(warn_for_aes_extract_usage(aes(.data[["x"]]), df))
143+
144+
# use of $ for a nested data frame column
145+
expect_silent(warn_for_aes_extract_usage(aes(nested_df$x), df))
146+
expect_silent(warn_for_aes_extract_usage(aes(nested_df[["x"]]), df))
147+
})
148+
149+
test_that("Warnings are issued when plots use discouraged extract usage within aes()", {
150+
df <- data_frame(x = 1:3, y = 1:3)
151+
p <- ggplot(df, aes(df$x, y)) + geom_point()
147152
expect_warning(ggplot_build(p), "Use of `df\\$x` is discouraged")
148153
})
149154

0 commit comments

Comments
 (0)