diff --git a/DESCRIPTION b/DESCRIPTION index 384c343..5b9076c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: tinyrox Title: Minimal R Documentation Generator -Version: 0.3.3.1 +Version: 0.3.3.2 Authors@R: person("Troy", "Hernandez", email = "troy@cornball.ai", role = c("aut", "cre"), comment = c(ORCID = "0009-0005-4248-604X")) diff --git a/NEWS.md b/NEWS.md index 0a8b72d..8d017e6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# tinyrox 0.3.3.2 + +* Fix false "undocumented parameters" warning for functions documented via a sibling block in an `@rdname` group; the check is now group-wide. Also gate the warning on `cran_check` rather than `silent` (#12). + # tinyrox 0.3.1 * Replace internal `utils:::.getHelpFile()` call with `tools::parse_Rd()` for CRAN compliance. diff --git a/R/document.R b/R/document.R index 611dad6..4a770d7 100644 --- a/R/document.R +++ b/R/document.R @@ -57,31 +57,47 @@ document <- function(path = ".", } # Parse all R files - if (!silent) message("Parsing R files...") + if (!silent) { + message("Parsing R files...") + } blocks <- parse_package(path) if (length(blocks) == 0) { - if (!silent) message("No documentation blocks found.") + if (!silent) { + message("No documentation blocks found.") + } return(invisible(list(rd_files = character(), namespace = NULL))) } - if (!silent) message("Found ", length(blocks), " documentation block(s).") + if (!silent) { + message("Found ", length(blocks), " documentation block(s).") + } # Generate Rd files - if (!silent) message("Generating Rd files...") - rd_files <- generate_all_rd(blocks, path, silent) - if (!silent) message("Generated ", length(rd_files), " Rd file(s).") + if (!silent) { + message("Generating Rd files...") + } + rd_files <- generate_all_rd(blocks, path, cran_check) + if (!silent) { + message("Generated ", length(rd_files), " Rd file(s).") + } # Generate NAMESPACE ns_file <- NULL if (namespace != "none") { - if (!silent) message("Generating NAMESPACE...") + if (!silent) { + message("Generating NAMESPACE...") + } ns_content <- generate_namespace(blocks) ns_file <- write_namespace(ns_content, path, namespace) - if (!silent) message("Updated NAMESPACE.") + if (!silent) { + message("Updated NAMESPACE.") + } } - if (!silent) message("Leaving DESCRIPTION alone as one should.") + if (!silent) { + message("Leaving DESCRIPTION alone as one should.") + } invisible(list(rd_files = rd_files, namespace = ns_file)) } diff --git a/R/rd.R b/R/rd.R index 7c385f9..8329784 100644 --- a/R/rd.R +++ b/R/rd.R @@ -669,10 +669,11 @@ generate_rd_grouped <- function(topic, entries, all_tags, #' #' @param blocks List of documentation blocks from parse_package(). #' @param path Package root path. -#' @param silent Boolean flag whether operation whould be silent +#' @param cran_check Emit CRAN-compliance warnings (undocumented +#' parameters). Default TRUE. #' @return Character vector of generated file paths. #' @keywords internal -generate_all_rd <- function(blocks, path = ".", silent = FALSE) { +generate_all_rd <- function(blocks, path = ".", cran_check = TRUE) { generated <- character() # Find package-defined S3 generics for proper \method{}{} usage formatting @@ -772,12 +773,13 @@ generate_all_rd <- function(blocks, path = ".", silent = FALSE) { generated <- c(generated, filepath) # Warn about undocumented params - if (block$type %in% c("function", "nn_module") && + if (cran_check && + block$type %in% c("function", "nn_module") && !is.null(block$formals)) { formal_names <- block$formals$names undoc <- setdiff(formal_names, names(tags$params)) undoc <- setdiff(undoc, "...") - if (length(undoc) > 0 && !silent) { + if (length(undoc) > 0) { warning("Undocumented parameters in ", tags$name, ": ", paste(undoc, collapse = ", "), call. = FALSE) @@ -789,17 +791,34 @@ generate_all_rd <- function(blocks, path = ".", silent = FALSE) { filepath <- write_rd(rd_content, topic, path) generated <- c(generated, filepath) - # Warn about undocumented params across all entries - for (entry in entries) { - if (entry$block$type %in% c("function", "nn_module") && - !is.null(entry$block$formals)) { - formal_names <- entry$block$formals$names - undoc <- setdiff(formal_names, names(entry$tags$params)) - undoc <- setdiff(undoc, "...") - if (length(undoc) > 0 && !silent) { - warning("Undocumented parameters in ", entry$tags$name, ": ", - paste(undoc, collapse = ", "), - call. = FALSE) + # Warn about undocumented params. Blocks sharing an @rdname + # merge into one page, so a parameter is documented if ANY + # block in the group documents it (matching the param merge in + # generate_rd_grouped()). Check each function's formals against + # the group-wide union, with @inheritParams resolved. + if (cran_check) { + documented <- character() + for (entry in entries) { + etags <- entry$tags + if (length(etags$inheritParams) > 0) { + etags <- resolve_inherit_params(etags, all_tags, + entry$block$formals) + } + documented <- c(documented, names(etags$params)) + } + documented <- unique(documented) + for (entry in entries) { + if (entry$block$type %in% c("function", "nn_module") && + !is.null(entry$block$formals)) { + formal_names <- entry$block$formals$names + undoc <- setdiff(formal_names, documented) + undoc <- setdiff(undoc, "...") + if (length(undoc) > 0) { + warning("Undocumented parameters in ", + entry$tags$name, ": ", + paste(undoc, collapse = ", "), + call. = FALSE) + } } } } diff --git a/inst/tinytest/test_rdname.R b/inst/tinytest/test_rdname.R index 43256fa..86e54c2 100644 --- a/inst/tinytest/test_rdname.R +++ b/inst/tinytest/test_rdname.R @@ -234,3 +234,79 @@ expect_true(grepl("Detail B", rd_c)) # Examples from both blocks expect_true(grepl("func_a\\(1\\)", rd_c)) expect_true(grepl("func_b\\(2\\)", rd_c)) + + +# --- Test 7: @rdname params documented on a sibling block (issue #12) --- +# A function whose formals are documented on the primary block (not its own) +# must NOT be flagged as having undocumented parameters: blocks sharing an +# @rdname merge into one page, so the check is group-wide. + +if (at_home()) { + tmp7 <- tempfile("rdname12") + dir.create(file.path(tmp7, "R"), recursive = TRUE, showWarnings = FALSE) + dir.create(file.path(tmp7, "man"), recursive = TRUE, showWarnings = FALSE) + writeLines(c("Package: rdname12", "Title: Test", "Version: 0.1", + "Description: Test pkg."), file.path(tmp7, "DESCRIPTION")) + + # Primary block documents utc + precision; set_format has those formals + # but documents none of its own. set_level documents its own `level`. + writeLines(c( + "#' Logger setup", + "#'", + "#' Configure the logger.", + "#' @param utc Use UTC timestamps.", + "#' @param precision Sub-second digits.", + "#' @rdname logsetup", + "#' @export", + "set_format <- function(utc, precision) invisible(NULL)", + "", + "#' @param level Logging threshold.", + "#' @rdname logsetup", + "#' @export", + "set_level <- function(level) invisible(NULL)" + ), file.path(tmp7, "R", "logsetup.R")) + + blocks7 <- tinyrox:::parse_package(tmp7) + + # No false positive: every formal is documented somewhere in the group. + expect_silent(tinyrox:::generate_all_rd(blocks7, tmp7, cran_check = TRUE)) + + unlink(tmp7, recursive = TRUE) +} + + +# --- Test 8: genuinely undocumented param still warns; cran_check gates it --- + +if (at_home()) { + tmp8 <- tempfile("rdnamewarn") + dir.create(file.path(tmp8, "R"), recursive = TRUE, showWarnings = FALSE) + dir.create(file.path(tmp8, "man"), recursive = TRUE, showWarnings = FALSE) + writeLines(c("Package: rdnamewarn", "Title: Test", "Version: 0.1", + "Description: Test pkg."), file.path(tmp8, "DESCRIPTION")) + + # `bogus` is documented by no block in the group -> should warn. + writeLines(c( + "#' Logger setup", + "#'", + "#' Configure the logger.", + "#' @param utc Use UTC timestamps.", + "#' @rdname logsetup", + "#' @export", + "set_format <- function(utc, bogus) invisible(NULL)", + "", + "#' @param level Logging threshold.", + "#' @rdname logsetup", + "#' @export", + "set_level <- function(level) invisible(NULL)" + ), file.path(tmp8, "R", "logsetup.R")) + + blocks8 <- tinyrox:::parse_package(tmp8) + + # Warns about the truly-undocumented `bogus`... + expect_warning(tinyrox:::generate_all_rd(blocks8, tmp8, cran_check = TRUE), + "bogus") + # ...but cran_check = FALSE silences it. + expect_silent(tinyrox:::generate_all_rd(blocks8, tmp8, cran_check = FALSE)) + + unlink(tmp8, recursive = TRUE) +} diff --git a/man/generate_all_rd.Rd b/man/generate_all_rd.Rd index 8b9f5b3..fca9d43 100644 --- a/man/generate_all_rd.Rd +++ b/man/generate_all_rd.Rd @@ -3,14 +3,15 @@ \alias{generate_all_rd} \title{Generate All Rd Files for a Package} \usage{ -generate_all_rd(blocks, path = ".", silent = FALSE) +generate_all_rd(blocks, path = ".", cran_check = TRUE) } \arguments{ \item{blocks}{List of documentation blocks from parse_package().} \item{path}{Package root path.} -\item{silent}{Boolean flag whether operation whould be silent} +\item{cran_check}{Emit CRAN-compliance warnings (undocumented +parameters). Default TRUE.} } \value{ Character vector of generated file paths.