Skip to content

Commit 6434c1e

Browse files
Warn on the use of ..var.. and stat(var) (#4950)
1 parent 32534e9 commit 6434c1e

File tree

11 files changed

+56
-32
lines changed

11 files changed

+56
-32
lines changed

DESCRIPTION

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Imports:
3636
grid,
3737
gtable (>= 0.1.1),
3838
isoband,
39-
lifecycle,
39+
lifecycle (> 1.0.1),
4040
MASS,
4141
mgcv,
4242
rlang (>= 1.0.0),
@@ -71,6 +71,8 @@ Suggests:
7171
testthat (>= 3.1.2),
7272
vdiffr (>= 1.0.0),
7373
xml2
74+
Remotes:
75+
r-lib/lifecycle
7476
Enhances:
7577
sp
7678
VignetteBuilder:

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# ggplot2 (development version)
22

3+
* The dot-dot notation (`..var..`) and `stat()`, which have been superseded by
4+
`after_stat()`, are now formally deprecated (@yutannihilation, #3693).
5+
36
* `geom_col()` and `geom_bar()` gain a new `just` argument. This is set to `0.5`
47
by default; use `just = 0`/`just = 1` to place columns on the left/right
58
of the axis breaks.

R/aes-evaluation.r

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,31 +93,44 @@ is_dotted_var <- function(x) {
9393
}
9494

9595
# Determine if aesthetic is calculated
96-
is_calculated_aes <- function(aesthetics) {
97-
vapply(aesthetics, is_calculated, logical(1), USE.NAMES = FALSE)
96+
is_calculated_aes <- function(aesthetics, warn = FALSE) {
97+
vapply(aesthetics, is_calculated, warn = warn, logical(1), USE.NAMES = FALSE)
9898
}
9999
is_scaled_aes <- function(aesthetics) {
100100
vapply(aesthetics, is_scaled, logical(1), USE.NAMES = FALSE)
101101
}
102102
is_staged_aes <- function(aesthetics) {
103103
vapply(aesthetics, is_staged, logical(1), USE.NAMES = FALSE)
104104
}
105-
is_calculated <- function(x) {
105+
is_calculated <- function(x, warn = FALSE) {
106106
if (is_call(get_expr(x), "after_stat")) {
107107
return(TRUE)
108108
}
109109
# Support of old recursive behaviour
110110
if (is.atomic(x)) {
111111
FALSE
112112
} else if (is.symbol(x)) {
113-
is_dotted_var(as.character(x))
113+
res <- is_dotted_var(as.character(x))
114+
if (res && warn) {
115+
what <- I(glue("The dot-dot notation (`{x}`)"))
116+
var <- gsub(match_calculated_aes, "\\1", as.character(x))
117+
with <- I(glue("`after_stat({var})`"))
118+
lifecycle::deprecate_warn("3.4.0", what, with, id = "ggplot-warn-aes-dot-dot")
119+
}
120+
res
114121
} else if (is_quosure(x)) {
115-
is_calculated(quo_get_expr(x))
122+
is_calculated(quo_get_expr(x), warn = warn)
116123
} else if (is.call(x)) {
117124
if (identical(x[[1]], quote(stat))) {
125+
if (warn) {
126+
what <- I(glue("`{expr_deparse(x)}`"))
127+
x[[1]] <- quote(after_stat)
128+
with <- I(glue("`{expr_deparse(x)}`"))
129+
lifecycle::deprecate_warn("3.4.0", what, with, id = "ggplot-warn-aes-stat")
130+
}
118131
TRUE
119132
} else {
120-
any(vapply(x, is_calculated, logical(1)))
133+
any(vapply(x, is_calculated, warn = warn, logical(1)))
121134
}
122135
} else if (is.pairlist(x)) {
123136
FALSE

R/layer.r

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,7 @@ layer <- function(geom = NULL, stat = NULL,
129129
if (geom$rename_size && "size" %in% extra_param && !"linewidth" %in% mapped_aesthetics(mapping)) {
130130
aes_params <- c(aes_params, params["size"])
131131
extra_param <- setdiff(extra_param, "size")
132-
# TODO: move to cli_warn()
133-
cli::cli_inform(c(
134-
"{.field size} aesthetic has been deprecated for use with lines as of ggplot2 3.4.0",
135-
"i" = "Please use {.field linewidth} aesthetic instead"
136-
), .frequency = "regularly", .frequency_id = "ggplot-size-linewidth")
132+
lifecycle::deprecate_warn("3.4.0", I("Using `size` aesthetic for lines"), I("`linewidth`"))
137133
}
138134
if (check.param && length(extra_param) > 0) {
139135
cli::cli_warn("Ignoring unknown parameters: {.arg {extra_param}}", call = call_env)
@@ -146,11 +142,7 @@ layer <- function(geom = NULL, stat = NULL,
146142
# Take care of size->linewidth aes renaming
147143
if (geom$rename_size && "size" %in% extra_aes && !"linewidth" %in% mapped_aesthetics(mapping)) {
148144
extra_aes <- setdiff(extra_aes, "size")
149-
# TODO: move to cli_warn()
150-
cli::cli_inform(c(
151-
"{.field size} aesthetic has been deprecated for use with lines as of ggplot2 3.4.0",
152-
"i" = "Please use {.field linewidth} aesthetic instead"
153-
), .frequency = "regularly", .frequency_id = "ggplot-size-linewidth")
145+
lifecycle::deprecate_warn("3.4.0", I("Using `size` aesthetic for lines"), I("`linewidth`"))
154146
}
155147
if (check.aes && length(extra_aes) > 0) {
156148
cli::cli_warn("Ignoring unknown aesthetics: {.field {extra_aes}}", call = call_env)
@@ -247,11 +239,7 @@ Layer <- ggproto("Layer", NULL,
247239
!"linewidth" %in% names(self$computed_mapping) &&
248240
"linewidth" %in% self$geom$aesthetics()) {
249241
self$computed_mapping$size <- plot$mapping$size
250-
# TODO: move to cli_warn()
251-
cli::cli_inform(c(
252-
"{.field size} aesthetic has been deprecated for use with lines as of ggplot2 3.4.0",
253-
"i" = "Please use {.field linewidth} aesthetic instead"
254-
), .frequency = "regularly", .frequency_id = "ggplot-size-linewidth")
242+
lifecycle::deprecate_warn("3.4.0", I("Using `size` aesthetic for lines"), I("`linewidth`"))
255243
}
256244
# defaults() strips class, but it needs to be preserved for now
257245
class(self$computed_mapping) <- "uneval"
@@ -267,7 +255,7 @@ Layer <- ggproto("Layer", NULL,
267255

268256
# Drop aesthetics that are set or calculated
269257
set <- names(aesthetics) %in% names(self$aes_params)
270-
calculated <- is_calculated_aes(aesthetics)
258+
calculated <- is_calculated_aes(aesthetics, warn = TRUE)
271259
modifiers <- is_scaled_aes(aesthetics)
272260

273261
aesthetics <- aesthetics[!set & !calculated & !modifiers]

tests/testthat/_snaps/aes-calculated.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,13 @@
66

77
Duplicated aesthetics after name standardisation: colour
88

9+
# A deprecated warning is issued when stat(var) or ..var.. is used
10+
11+
`stat(foo)` was deprecated in ggplot2 3.4.0.
12+
Please use `after_stat(foo)` instead.
13+
14+
---
15+
16+
The dot-dot notation (`..bar..`) was deprecated in ggplot2 3.4.0.
17+
Please use `after_stat(bar)` instead.
18+

tests/testthat/_snaps/layer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
x `fill = after_stat(data)`
4848
i Did you map your stat in the wrong layer?
4949

50-
# function aesthetics are wrapped with stat()
50+
# function aesthetics are wrapped with after_stat()
5151

5252
Problem while computing aesthetics.
5353
i Error occurred in the 1st layer.

tests/testthat/test-aes-calculated.r

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,11 @@ test_that("staged aesthetics warn appropriately for duplicated names", {
6969
# One warning in building due to `stage()`/`after_scale()`
7070
expect_snapshot_warning(ggplot_build(p))
7171
})
72+
73+
test_that("A deprecated warning is issued when stat(var) or ..var.. is used", {
74+
p1 <- ggplot(NULL, aes(stat(foo)))
75+
expect_snapshot_warning(b1 <- ggplot_build(p1))
76+
77+
p2 <- ggplot(NULL, aes(..bar..))
78+
expect_snapshot_warning(b2 <- ggplot_build(p2))
79+
})

tests/testthat/test-geom-sf.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,18 @@ test_that("geom_sf() removes rows containing missing aes", {
9999
colour = c("red", NA)
100100
)
101101

102-
p <- ggplot(pts) + geom_sf()
102+
p <- ggplot(pts)
103103
expect_warning(
104-
expect_identical(grob_xy_length(p + aes(size = size)), c(1L, 1L)),
104+
expect_identical(grob_xy_length(p + geom_sf(aes(size = size))), c(1L, 1L)),
105105
"Removed 1 row containing missing values"
106106
)
107107
expect_warning(
108-
expect_identical(grob_xy_length(p + aes(shape = shape)), c(1L, 1L)),
108+
expect_identical(grob_xy_length(p + geom_sf(aes(shape = shape))), c(1L, 1L)),
109109
"Removed 1 row containing missing values"
110110
)
111111
# default colour scale maps a colour even to a NA, so identity scale is needed to see if NA is removed
112112
expect_warning(
113-
expect_identical(grob_xy_length(p + aes(colour = colour) + scale_colour_identity()),
113+
expect_identical(grob_xy_length(p + geom_sf(aes(colour = colour)) + scale_colour_identity()),
114114
c(1L, 1L)),
115115
"Removed 1 row containing missing values"
116116
)

tests/testthat/test-layer.r

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ test_that("missing aesthetics trigger informative error", {
5555
)
5656
})
5757

58-
test_that("function aesthetics are wrapped with stat()", {
58+
test_that("function aesthetics are wrapped with after_stat()", {
5959
df <- data_frame(x = 1:10)
6060
expect_snapshot_error(
6161
ggplot_build(ggplot(df, aes(colour = density, fill = density)) + geom_point())
@@ -65,7 +65,7 @@ test_that("function aesthetics are wrapped with stat()", {
6565
test_that("computed stats are in appropriate layer", {
6666
df <- data_frame(x = 1:10)
6767
expect_snapshot_error(
68-
ggplot_build(ggplot(df, aes(colour = stat(density), fill = stat(density))) + geom_point())
68+
ggplot_build(ggplot(df, aes(colour = after_stat(density), fill = after_stat(density))) + geom_point())
6969
)
7070
})
7171

tests/testthat/test-stat-contour.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ test_that("geom_contour() and stat_contour() result in identical layer data", {
6565

6666
test_that("basic stat_contour() plot builds", {
6767
p <- ggplot(faithfuld, aes(waiting, eruptions)) +
68-
geom_contour(aes(z = density, col = factor(stat(level))))
68+
geom_contour(aes(z = density, col = factor(after_stat(level))))
6969

7070
# stat_contour() visual tests are unstable due to the
7171
# implementation in isoband

tests/testthat/test-stat-sf-coordinates.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ test_that("stat_sf_coordinates() retrieves coordinates from sf objects", {
2828
# computed variables (x and y)
2929
df_point <- sf::st_sf(geometry = sf::st_sfc(sf::st_point(c(1, 2))))
3030
expect_identical(
31-
comp_sf_coord(df_point, aes(x = stat(x) + 10, y = stat(y) * 10))[, c("x", "y")],
31+
comp_sf_coord(df_point, aes(x = after_stat(x) + 10, y = after_stat(y) * 10))[, c("x", "y")],
3232
data_frame(x = 11, y = 20)
3333
)
3434
})

0 commit comments

Comments
 (0)