diff --git a/NEWS.md b/NEWS.md index 1ee8fbc7..e1ecc21a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # roxygen2 (development version) +* A friendlier error is thrown when attempting to import non-existing + functions with `@importFrom` (#1409, @MichaelChirico). * authors in `DESCRIPTION` can now have multiple email addresses (@jmbarbone, #1487). * The `ROXYGEN_PKG` environment variable is now set up while roxygen diff --git a/R/namespace.R b/R/namespace.R index 19be1eff..96187207 100644 --- a/R/namespace.R +++ b/R/namespace.R @@ -238,6 +238,15 @@ roxy_tag_parse.roxy_tag_importFrom <- function(x) { } #' @export roxy_tag_ns.roxy_tag_importFrom <- function(x, block, env, import_only = FALSE) { + pkg <- x$val[1L] + if (requireNamespace(pkg, quietly = TRUE)) { + importing <- x$val[-1L] + unknown_idx <- !importing %in% getNamespaceExports(pkg) + if (any(unknown_idx)) { + warn_roxy_tag(x, "Excluding unknown {cli::qty(sum(unknown_idx))} export{?s} in from {.package {pkg}}: {.code {importing[unknown_idx]}}") + x$val <- c(pkg, importing[!unknown_idx]) + } + } repeat_first_ignore_current("importFrom", x$val) } diff --git a/tests/testthat/test-namespace.R b/tests/testthat/test-namespace.R index 6eebfd40..71537c37 100644 --- a/tests/testthat/test-namespace.R +++ b/tests/testthat/test-namespace.R @@ -378,3 +378,38 @@ test_that("can extract non-imports from namespace preserving source", { path <- withr::local_tempfile(lines = lines) expect_equal(namespace_exports(path), lines[c(1:3, 5)]) }) + +test_that("Invalid imports throw a helpful error", { + expect_warning( + expect_equal( + roc_proc_text(namespace_roclet(), " + #' @importFrom utils head InvalidUtilsFunction + NULL + "), + "importFrom(utils,head)" + ), + "Excluding unknown export", + fixed = TRUE + ) + + # pluralization + expect_warning( + expect_equal( + roc_proc_text(namespace_roclet(), " + #' @importFrom utils head InvalidUtilsFunction1 InvalidUtilsFunction2 + NULL + "), + "importFrom(utils,head)" + ), + "Excluding unknown exports" + ) + + # If the package is not available at roxygenize() run time, nothing we can do + expect_equal( + roc_proc_text(namespace_roclet(), " + #' @importFrom AnUnknownUnavailablePackage Unchecked + NULL + "), + "importFrom(AnUnknownUnavailablePackage,Unchecked)" + ) +})