diff --git a/NEWS.md b/NEWS.md index de3e87cee3..cc0f6931c2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # ggplot2 (development version) +* Scales now throw warnings when no matching data is detected + (@teunbrand, #3743) * The `arrow.fill` parameter is now applied to more line-based functions: `geom_path()`, `geom_line()`, `geom_step()` `geom_function()`, line geometries in `geom_sf()` and `element_line()`. diff --git a/R/plot-build.R b/R/plot-build.R index 7b90d9cf26..f97f4b7354 100644 --- a/R/plot-build.R +++ b/R/plot-build.R @@ -102,7 +102,7 @@ ggplot_build.ggplot <- function(plot) { # Train and map non-position scales and guides npscales <- scales$non_position_scales() if (npscales$n() > 0) { - lapply(data, npscales$train_df) + npscales$train_data(data) plot$guides <- plot$guides$build(npscales, plot$layers, plot$labels, data) data <- lapply(data, npscales$map_df) } else { diff --git a/R/scales-.R b/R/scales-.R index e62eb0e8cb..4623b58c94 100644 --- a/R/scales-.R +++ b/R/scales-.R @@ -68,6 +68,20 @@ ScalesList <- ggproto("ScalesList", NULL, lapply(self$scales, function(scale) scale$train_df(df = df)) }, + train_data = function(self, data) { + lapply(data, self$train_df) + data_names <- unique0(unlist(lapply(data, colnames))) + for (scale in self$scales) { + if (!any(scale$aesthetics %in% data_names)) { + cli::cli_warn( + "Scale for {.field {scale$aesthetics}} aesthetic{?s} was provided \\ + but has no matching data to map.", + call = scale$call + ) + } + } + }, + map_df = function(self, df) { if (empty(df) || length(self$scales) == 0) { return(df) diff --git a/tests/testthat/test-scales.R b/tests/testthat/test-scales.R index 4e104f9024..44eeeccd22 100644 --- a/tests/testthat/test-scales.R +++ b/tests/testthat/test-scales.R @@ -71,6 +71,15 @@ test_that("identity scale preserves input values", { expect_equal(d1, d2) }) +test_that("a warning is thrown when a scale is not used", { + p <- ggplot(mtcars, aes(disp, mpg)) + geom_point() + scale_colour_discrete() + + expect_warning( + ggplot_build(p), + "was provided but has no matching data to map" + ) +}) + test_that("position scales are updated by all position aesthetics", { df <- data_frame(x = 1:3, y = 1:3)