Skip to content

Commit 1f009e8

Browse files
authored
Add argument selection to @inheritParams (#1852)
Fixes #1849
1 parent ad628d3 commit 1f009e8

8 files changed

Lines changed: 148 additions & 13 deletions

File tree

NEWS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* S7 methods registered with `method(generic, class) <- fn` are detected automatically and generate usage with `## S7 method for class <ClassName>`.
77
* roxygen2 no longer depends on stringr/stringi. This means that no package in the devtools constellation depends on stringr, which in turn means you no longer need stringi, making it a bit easier to install in constrained Linux environments.
88
* roxygen2 options can now be set using `Config/roxygen2/` fields in DESCRIPTION (e.g. `Config/roxygen2/markdown: TRUE`) instead of the `Roxygen` field. The old `Roxygen` field is still supported. Similarly, the roxygen2 version is now stored in `Config/roxygen2/version` instead of `RoxygenNote` (#1328).
9-
* Tags that expect single-line input now warn when they span multiple lines, catching common mistakes. Affected tags: `@aliases`, `@concept`, `@encoding`, `@exportClass`, `@exportMethod`, `@exportPattern`, `@exportS3Method`, `@importFrom`, `@importClassesFrom`, `@importMethodsFrom`, `@include`, `@inheritParams`, `@keywords`, `@method`, `@name`, `@order`, `@rdname`, `@S3method`, `@template`, and `@useDynLib` (#1642, #1688). This may break some existing usage, but it prevents a wide class of otherwise silent errors.
9+
* Tags that expect single-line input now warn when they span multiple lines, catching common mistakes. Affected tags: `@aliases`, `@concept`, `@encoding`, `@exportClass`, `@exportMethod`, `@exportPattern`, `@exportS3Method`, `@importFrom`, `@importClassesFrom`, `@importMethodsFrom`, `@include`, `@keywords`, `@method`, `@name`, `@order`, `@rdname`, `@S3method`, `@template`, and `@useDynLib` (#1642, #1688). This may break some existing usage, but it prevents a wide class of otherwise silent errors.
10+
* `@inheritParams` now supports argument filtering using the same syntax as `@inheritDotParams`. For example, `@inheritParams foo x y` inherits only `x` and `y`, and `@inheritParams foo -z` inherits everything except `z` (#1849).
1011
* `@examplesIf` now warns when there is no example code after the condition (#1695).
1112
* `tag_words_line()` is deprecated in favour of `tag_words()`, which now checks for single-line content by default. Use `tag_words(x, multiline = TRUE)` or `tag_value(x, multiline = TRUE)` if your tag legitimately spans multiple lines.
1213
* R6 improvements:

R/rd-inherit.R

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,21 @@ roxy_tag_rd.roxy_tag_inherit <- function(x, base_path, env) {
88
}
99

1010
#' @export
11-
roxy_tag_parse.roxy_tag_inheritParams <- function(x) tag_value(x)
11+
roxy_tag_parse.roxy_tag_inheritParams <- function(x) {
12+
tag_two_part(
13+
x,
14+
"a source",
15+
"an argument list",
16+
required = FALSE,
17+
markdown = FALSE
18+
)
19+
}
1220
#' @export
1321
roxy_tag_rd.roxy_tag_inheritParams <- function(x, base_path, env) {
14-
rd_section_inherit(x$val, list("params"))
22+
list(
23+
rd_section_inherit(x$val$name, list("params")),
24+
rd_section_inherit_params_args(x$val$name, x$val$description)
25+
)
1526
}
1627

1728
#' @export
@@ -107,6 +118,28 @@ merge.rd_section_inherit_dot_params <- function(x, y, ...) {
107118
)
108119
}
109120

121+
rd_section_inherit_params_args <- function(source, args) {
122+
check_string(source)
123+
check_string(args)
124+
125+
if (!nzchar(args)) {
126+
return(NULL)
127+
}
128+
rd_section("inherit_params_args", list(source = source, args = args))
129+
}
130+
131+
#' @export
132+
format.rd_section_inherit_params_args <- function(x, ...) NULL
133+
134+
#' @export
135+
merge.rd_section_inherit_params_args <- function(x, y, ...) {
136+
stopifnot(identical(class(x), class(y)))
137+
rd_section_inherit_params_args(
138+
c(x$value$source, y$value$source),
139+
c(x$value$args, y$value$args)
140+
)
141+
}
142+
110143

111144
# Process inheritance -----------------------------------------------------
112145

@@ -168,13 +201,26 @@ inherit_params <- function(topic, topics) {
168201
# Work through inherited params seeing if any match the parameters
169202
# we're missing
170203
for (inheritor in inheritors) {
204+
source <- topic$get_name()
171205
inherited_params <- find_params(
172206
inheritor,
173207
topics,
174-
source = topic$get_name(),
208+
source = source,
175209
tag = "@inheritParams"
176210
)
177211

212+
# Apply argument filter if specified via @inheritParams foo args
213+
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 != "") {
216+
doc_args <- map_chr(inherited_params, "[[", "name")
217+
selected <- select_args_text(doc_args, args_filter, topic_name = source)
218+
inherited_params <- Filter(
219+
function(p) any(p$name %in% selected),
220+
inherited_params
221+
)
222+
}
223+
178224
for (param in inherited_params) {
179225
match <- match_param(param$name, missing)
180226
if (!is.null(match)) {

R/select-args.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ select_args_text <- function(args, select, topic_name) {
1111
select_args(args, parsed)
1212
},
1313
error = function(e) {
14-
warn_roxy_topic(topic_name, "@inheritDotsParam failed", parent = e)
14+
warn_roxy_topic(topic_name, "argument selection failed", parent = e)
1515
character()
1616
}
1717
)

inst/roxygen2-tags.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,9 @@
228228
description: >
229229
Inherit argument documentation from another function. Only inherits
230230
documentation for arguments that aren't already documented locally.
231-
template: ' ${1:source}'
231+
Optionally followed by a list of argument names to include or exclude,
232+
using the same syntax as `@inheritDotParams`.
233+
template: ' ${1:source} ${2:arg1 arg2 arg3}'
232234
vignette: reuse
233235
recommend: true
234236

tests/testthat/_snaps/rd-inherit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
Code
7171
. <- roc_proc_text(rd_roclet(), text)
7272
Message
73-
x In topic 'bar': @inheritDotsParam failed.
73+
x In topic 'bar': argument selection failed.
7474
Caused by error in `FUN()`:
7575
! object 'z' not found
7676
x In topic 'bar': @inheritDotParams failed.

tests/testthat/_snaps/select-args.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Code
44
select_args_text(c("x", "na.rm"), "-xlab:", "test")
55
Message
6-
x In topic 'test': @inheritDotsParam failed.
6+
x In topic 'test': argument selection failed.
77
Caused by error in `parse()`:
88
! <text>:2:0: unexpected end of input
99
1: -xlab:
@@ -13,7 +13,7 @@
1313
Code
1414
select_args_text(c("x", "na.rm"), "\"a\"", "test")
1515
Message
16-
x In topic 'test': @inheritDotsParam failed.
16+
x In topic 'test': argument selection failed.
1717
Caused by error in `select_check()`:
1818
! Argument specification must evaluate to a numeric vector.
1919
Problem in `"a"`.
@@ -22,7 +22,7 @@
2222
Code
2323
select_args_text(c("x", "y", "z"), "-x:z", "test")
2424
Message
25-
x In topic 'test': @inheritDotsParam failed.
25+
x In topic 'test': argument selection failed.
2626
Caused by error:
2727
! Argument specification must be all positive or all negative, not a mixture.
2828
i Problem in `-x:z`.

tests/testthat/test-rd-inherit.R

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,88 @@ test_that("inherit params ... named \\dots", {
676676
)
677677
})
678678

679+
# inheritParams argument filtering ----------------------------------------
680+
681+
test_that("@inheritParams can include specific args", {
682+
out <- roc_proc_text(
683+
rd_roclet(),
684+
"
685+
#' A.
686+
#'
687+
#' @param x X
688+
#' @param y Y
689+
#' @param z Z
690+
a <- function(x, y, z) {}
691+
692+
#' B
693+
#'
694+
#' @inheritParams a x z
695+
b <- function(x, y, z) {}
696+
"
697+
)[[2]]
698+
699+
params <- out$get_value("param")
700+
expect_equal(params, c(x = "X", z = "Z"))
701+
})
702+
703+
test_that("@inheritParams can exclude specific args", {
704+
out <- roc_proc_text(
705+
rd_roclet(),
706+
"
707+
#' A.
708+
#'
709+
#' @param x X
710+
#' @param y Y
711+
#' @param z Z
712+
a <- function(x, y, z) {}
713+
714+
#' B
715+
#'
716+
#' @inheritParams a -y
717+
b <- function(x, y, z) {}
718+
"
719+
)[[2]]
720+
721+
params <- out$get_value("param")
722+
expect_equal(params, c(x = "X", z = "Z"))
723+
})
724+
725+
test_that("@inheritParams filtering works with external packages", {
726+
out <- roc_proc_text(
727+
rd_roclet(),
728+
"
729+
#' My mean
730+
#'
731+
#' @inheritParams base::mean -na.rm
732+
mymean <- function(x, trim, na.rm) {}"
733+
)[[1]]
734+
735+
params <- out$get_value("param")
736+
expect_true("trim" %in% names(params))
737+
expect_false("na.rm" %in% names(params))
738+
})
739+
740+
test_that("@inheritParams without args still works", {
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+
#' @inheritParams a
753+
b <- function(x, y) {}
754+
"
755+
)[[2]]
756+
757+
params <- out$get_value("param")
758+
expect_equal(params, c(x = "X", y = "Y"))
759+
})
760+
679761
# inheritDotParams --------------------------------------------------------
680762

681763
test_that("can inherit all from single function", {

vignettes/reuse.Rmd

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,13 @@ Since no other function in dplyr has an argument with this name, its documentati
181181

182182
### Recursive inheritance
183183

184-
`@inheritParams` (like all `@inherits` functions) works recursively, so if `g` inherits parameters from `h`, then `f` can also inherit those parameters from `g`.
185-
However, this technique is best used sparingly: it's very easy to create complex dependency webs that are hard to reason about where making changing the documentation in one function cascades out in unexpected ways across your package.
186-
If you want to be more explicit, you should consider writing helper functions and using inline R code, as described below.
184+
`@inheritParams` (like all `@inherits` functions) works recursively, so if `g` inherits parameters from `h`, then `f` can also inherit those parameters from `g`.
185+
However, this technique is best used sparingly: it's very easy to create complex dependency webs that are hard to reason about where making changing the documentation in one function cascades out in unexpected ways across your package.
186+
You can avoid this problem by being more explicit about which parameters are inherited:
187+
188+
- `@inheritParams foo x y` inherits only `x` and `y`.
189+
- `@inheritParams foo -z` inherits all parameters except `z`.
190+
- `@inheritParams foo first:third` inherits parameters `first` through `third` (in argument order).
187191

188192
### Multiple parameters
189193

0 commit comments

Comments
 (0)