diff --git a/NEWS.md b/NEWS.md index 0fd950dcf6..a677367157 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,8 @@ * Allow `stat` in `geom_hline`, `geom_vline`, and `geom_abline`. (@sierrajohnson, #6559) +* Circularly defined `ggproto()` classes will throw more informative warning + (@teunbrand, #6583). # ggplot2 4.0.0 diff --git a/R/ggproto.R b/R/ggproto.R index 853a440f9f..83e060c4b9 100644 --- a/R/ggproto.R +++ b/R/ggproto.R @@ -131,7 +131,14 @@ fetch_ggproto <- function(x, name) { if (is.null(super)) { # no super class } else if (is.function(super)) { - res <- fetch_ggproto(super(), name) + parent <- super() + if (!identical(parent, x)) { + # happy path + return(fetch_ggproto(parent, name)) + } + cli::cli_abort( + "{.cls {class(x)[1]}} cannot have a circular definition." + ) } else { cli::cli_abort(c( "{class(x)[[1]]} was built with an incompatible version of ggproto.", diff --git a/tests/testthat/_snaps/ggproto.md b/tests/testthat/_snaps/ggproto.md index 365bcce1df..cc19f5b1cf 100644 --- a/tests/testthat/_snaps/ggproto.md +++ b/tests/testthat/_snaps/ggproto.md @@ -10,3 +10,7 @@ `_inherit` must be a object, not a object. +# circular definitions are protested + + cannot have a circular definition. + diff --git a/tests/testthat/test-ggproto.R b/tests/testthat/test-ggproto.R index baad887619..9a3e9a659e 100644 --- a/tests/testthat/test-ggproto.R +++ b/tests/testthat/test-ggproto.R @@ -11,6 +11,12 @@ test_that("construction checks input", { expect_snapshot_error(ggproto("Test", mtcars, a = function(self, a) a)) }) +test_that("circular definitions are protested", { + circular <- ggproto("Circular") + circular <- ggproto(NULL, circular) + expect_snapshot_error(print(circular)) +}) + test_that("all ggproto methods start with `{` (#6459)", { ggprotos <- Filter(