Skip to content

Commit 7ad9bee

Browse files
authored
Fix @inheritParams with multiple filtered tags (#1910)
Merging two `inherit_params_args` sections concatenates their sources and args, but the constructor used `check_string()`, so a second filtered `@inheritParams` always errored. It is now vectorised, like `rd_section_inherit_dot_params()`. The filter lookup also required exactly one match. Sources are deduplicated on merge, so repeating one (`@inheritParams a x` plus `@inheritParams a z`) gave a length-2 filter that was silently ignored; selections are now combined. Fixes #1879
1 parent 0db02fd commit 7ad9bee

3 files changed

Lines changed: 77 additions & 8 deletions

File tree

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* The automatic usage for a data object that is conditional on the `LazyData` option in the `DESCRIPTION` (see below) now correctly detects all ways to specify a true value, e.g. also `yes`, `Yes` or `True` (@jranke, #1881).
1212
* `@import` now inserts the directive as is into `NAMESPACE` when it contains a comma, making it possible to use other forms like `@import rlang, except = ":="`.
1313
* `@importFrom` now generates a single multiline `importFrom()` directive per package instead of one directive per symbol. This fixes a performance issue with `loadNamespace()` for packages that import many symbols.
14+
* `@inheritParams` no longer errors when a topic uses argument selection in more than one tag, e.g. `@inheritParams a x` followed by `@inheritParams b y` (#1879). If the same source is used in multiple tags, the union of their selections is now inherited instead of the selection being ignored, so an unfiltered tag inherits every parameter.
1415
* `@importFrom`, `@importClassesFrom`, and `@importMethodsFrom` now accept multi-line input, restoring the ability to spread imports across multiple lines for readability; continuation lines must use a hanging indent, so the first flush or blank line ends the tag and content after it (e.g. from a forgotten `@examples`) is no longer silently absorbed into the namespace (#1890).
1516

1617
# roxygen2 8.0.0

R/rd-inherit.R

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ merge.rd_section_inherit_dot_params <- function(x, y, ...) {
119119
}
120120

121121
rd_section_inherit_params_args <- function(source, args) {
122-
check_string(source)
123-
check_string(args)
122+
check_character(source)
123+
check_character(args)
124+
stopifnot(length(source) == length(args))
124125

125-
if (!nzchar(args)) {
126-
return(NULL)
127-
}
126+
# Empty args are retained: an unfiltered tag inherits every parameter, even
127+
# if another tag filters the same source.
128128
rd_section("inherit_params_args", list(source = source, args = args))
129129
}
130130

@@ -211,10 +211,14 @@ inherit_params <- function(topic, topics) {
211211

212212
# Apply argument filter if specified via @inheritParams foo args
213213
params_args <- topic$get_value("inherit_params_args")
214-
args_filter <- params_args$args[params_args$source == inheritor]
215-
if (length(args_filter) == 1 && args_filter != "") {
214+
args_filters <- params_args$args[params_args$source == inheritor]
215+
# Each tag selects independently; a source used in multiple tags inherits
216+
# the union of their selections, so an unfiltered tag inherits everything.
217+
if (length(args_filters) > 0 && all(nzchar(args_filters))) {
216218
doc_args <- map_chr(inherited_params, "[[", "name")
217-
selected <- select_args_text(doc_args, args_filter, topic_name = source)
219+
selected <- unlist(lapply(args_filters, function(args_filter) {
220+
select_args_text(doc_args, args_filter, topic_name = source)
221+
}))
218222
inherited_params <- Filter(
219223
function(p) any(p$name %in% selected),
220224
inherited_params

tests/testthat/test-rd-inherit.R

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,70 @@ test_that("@inheritParams filtering works with external packages", {
737737
expect_false("na.rm" %in% names(params))
738738
})
739739

740+
test_that("multiple @inheritParams can each filter args (#1879)", {
741+
out <- roc_proc_text(
742+
rd_roclet(),
743+
"
744+
#' A.
745+
#'
746+
#' @param x X
747+
#' @param y Y
748+
a <- function(x, y) {}
749+
750+
#' B.
751+
#'
752+
#' @param z Z
753+
#' @param w W
754+
b <- function(z, w) {}
755+
756+
#' C
757+
#'
758+
#' @inheritParams a x
759+
#' @inheritParams b -w
760+
c <- function(x, y, z, w) {}
761+
"
762+
)[[3]]
763+
764+
params <- out$get_value("param")
765+
expect_equal(params, c(x = "X", z = "Z"))
766+
})
767+
768+
test_that("@inheritParams filters are unioned for a repeated source", {
769+
filter <- function(...) {
770+
tags <- paste0(" #' @inheritParams ", c(...), collapse = "\n")
771+
out <- roc_proc_text(
772+
rd_roclet(),
773+
paste0(
774+
"
775+
#' A.
776+
#'
777+
#' @param x X
778+
#' @param y Y
779+
#' @param z Z
780+
a <- function(x, y, z) {}
781+
782+
#' B
783+
#'
784+
",
785+
tags,
786+
"
787+
b <- function(x, y, z) {}
788+
"
789+
)
790+
)[[2]]
791+
out$get_value("param")
792+
}
793+
794+
expect_equal(filter("a x", "a z"), c(x = "X", z = "Z"))
795+
expect_equal(filter("a x", "a -y"), c(x = "X", z = "Z"))
796+
# Each tag selects independently, so the order of tags doesn't matter
797+
expect_equal(filter("a -y", "a x"), c(x = "X", z = "Z"))
798+
expect_equal(filter("a x", "a -x"), c(x = "X", y = "Y", z = "Z"))
799+
# An unfiltered tag selects everything
800+
expect_equal(filter("a x", "a"), c(x = "X", y = "Y", z = "Z"))
801+
expect_equal(filter("a", "a x"), c(x = "X", y = "Y", z = "Z"))
802+
})
803+
740804
test_that("@inheritParams without args still works", {
741805
out <- roc_proc_text(
742806
rd_roclet(),

0 commit comments

Comments
 (0)