Skip to content

Commit c5faf32

Browse files
authored
Support method-level @returns (#1818)
Includes some refactoring to make it clear that there are three types of R6 tags: tags that belong to the class, tags that belong to a method, and tags that are handled elsewhere. Also fixes a dev-only bug that caused method descriptions/details to get repeated in the class docs. Fixes #1148
1 parent 2827626 commit c5faf32

8 files changed

Lines changed: 69 additions & 80 deletions

File tree

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* `@examplesIf` now warns when there is no example code after the condition (#1695).
44
* `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.
55
* R6 improvements:
6+
* `@returns` now works as a method-level tag in R6 classes, just like `@return` (#1148).
67
* The "Super classes" section now omits the `pkg::` prefix for parent classes from the same package, making the inheritance chain easier to read (#1567).
78
* R6 classes with only active bindings and `cloneable = FALSE` no longer error during documentation (#1610).
89
* `@example` (singular, with a file path) now works correctly in R6 class documentation (#1158).

R/rd-r6-methods-self.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ r6_method_from_row <- function(method, alias, block) {
119119

120120
params <- r6_resolve_params(method, block)
121121

122-
ret_tags <- keep(tags, \(t) t$tag == "return")
122+
ret_tags <- keep(tags, \(t) t$tag %in% c("return", "returns"))
123123
if (length(ret_tags) > 1) {
124-
warn_roxy_block(block, "Must use one @return per R6 method")
124+
warn_roxy_block(block, "Must use one @return(s) per R6 method")
125125
}
126126
ret <- if (length(ret_tags) > 0) ret_tags[[1]]$val else NULL
127127

R/rd-r6-methods.R

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,9 @@ r6_extract_methods <- function(r6data, alias, block) {
4343
methods_df$tags <- replicate(nrow(methods_df), list(), simplify = FALSE)
4444

4545
# Associate inline tags with methods
46-
r6_tags <- c("description", "details", "param", "return", "examples")
4746
for (i in seq_along(block$tags)) {
4847
tag <- block$tags[[i]]
49-
if (is.na(tag$line) || tag$line < block$line) {
50-
next
51-
}
52-
if (!tag$tag %in% r6_tags) {
48+
if (r6_tag_type(tag, block) != "method") {
5349
next
5450
}
5551
meth <- find_method_for_tag(methods_df, tag)

R/rd-r6.R

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ topic_add_r6_methods <- function(rd, block, env, base_path) {
33

44
# Add class-level tags
55
for (tag in block$tags) {
6-
if (is_class_tag(tag, block)) {
6+
if (r6_tag_type(tag, block) == "class") {
77
rd$add(roxy_tag_rd(tag, env = env, base_path = base_path))
88
}
99
}
@@ -20,12 +20,23 @@ topic_add_r6_methods <- function(rd, block, env, base_path) {
2020
}
2121
}
2222

23-
is_class_tag <- function(tag, block) {
24-
if (tag$tag %in% c("param", "field")) {
25-
FALSE
26-
} else if (tag$tag %in% c("description", "details", "return", "examples")) {
27-
!is.na(tag$line) && tag$line >= block$line
23+
# Classify an R6 block tag:
24+
# - "class": top-level Rd (e.g. @title, @description before class body)
25+
# - "method": inline tag associated with a method
26+
# - "other": @field/@param tags consumed by field/param extraction
27+
r6_tag_type <- function(tag, block) {
28+
inline <- !is.na(tag$line) && tag$line >= block$line
29+
method_tags <- c(
30+
"description", "details", "param", "return", "returns", "examples"
31+
)
32+
33+
if (tag$tag == "field") {
34+
"other"
35+
} else if (tag$tag %in% method_tags && inline) {
36+
"method"
37+
} else if (tag$tag == "param") {
38+
"other"
2839
} else {
29-
TRUE
40+
"class"
3041
}
3142
}

man/RoxyTopic.Rd

Lines changed: 1 addition & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/rd-r6-methods-self.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# warns about multiple @return(s) tags
2+
3+
Code
4+
docs <- r6_doc(text)
5+
Message
6+
x <text>:3: Must use one @return(s) per R6 method.
7+
18
# warns about undocumented params
29

310
Code

tests/testthat/_snaps/rd-r6.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,14 @@
88
\name{A}
99
\alias{A}
1010
\title{Class A}
11-
\value{
12-
A value.
13-
}
1411
\description{
15-
A method 1.
16-
17-
Method 2 description.
18-
19-
Method 3.
12+
Class A description.
2013
}
2114
\details{
22-
Method 2 details.
15+
Class A details
2316
}
2417
\examples{
2518
a <- A$new()
26-
## Example for meth1
27-
## Example for meth2
2819
2920
## ------------------------------------------------
3021
## Method `A$meth1`
@@ -185,9 +176,10 @@
185176
\alias{B}
186177
\title{Class B}
187178
\description{
188-
B method 1.
189-
190-
B method 4.
179+
Class B Description.
180+
}
181+
\details{
182+
Class B details.
191183
}
192184
\section{Super class}{
193185
\code{A} -> \code{B}
@@ -291,9 +283,10 @@
291283
\alias{C}
292284
\title{Class C}
293285
\description{
294-
C method 2.
295-
296-
C method 5.
286+
Class C Description.
287+
}
288+
\details{
289+
Class C details.
297290
}
298291
\section{Super classes}{
299292
\code{A} -> \code{B} -> \code{C}
@@ -366,4 +359,3 @@
366359
367360
}
368361
}
369-

tests/testthat/test-rd-r6-methods-self.R

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@ test_that("r6_method_from_row extracts all components", {
2424
expect_equal(method$examples, "c$greet('world')")
2525
})
2626

27+
test_that("@returns works as method-level tag (#1148)", {
28+
text <- "
29+
#' Class
30+
C <- R6::R6Class('C', cloneable = FALSE,
31+
public = list(
32+
#' @description Run.
33+
#' @returns The result.
34+
run = function() 1
35+
)
36+
)"
37+
docs <- r6_doc(text)
38+
expect_equal(docs$methods$self[[1]]$return, "The result.")
39+
})
40+
41+
test_that("warns about multiple @return(s) tags", {
42+
text <- "
43+
#' Class
44+
C <- R6::R6Class('C', cloneable = FALSE,
45+
public = list(
46+
#' @description Run.
47+
#' @return First.
48+
#' @returns Second.
49+
run = function() 1
50+
)
51+
)"
52+
expect_snapshot(docs <- r6_doc(text))
53+
expect_equal(docs$methods$self[[1]]$return, "First.")
54+
})
55+
2756
test_that("warns about undocumented params", {
2857
text <- "
2958
#' Class

0 commit comments

Comments
 (0)