I'm refactoring a package containing many non-exported functions and other non-exported objects. Is there a way to quickly identify all the "orphaned" objects that are defined but not exported or called anywhere else in the package, other than manually doing a full text search for the name each function in the package?
An additional complication is that some functions are called inside glue
f-style strings, so there may be cases where functions are called that are not parsable as expressions (this is probably not a good design pattern).
But I would be satisfied with a method for just functions and other objects that never appear as normal expressions after being defined.
This will give you a partial answer (edited from the original):
pkg <- "testpkg"
library(pkg, character.only = TRUE)
ns <- getNamespace(pkg)
allnames <- ls(ns)
exports <- ls(paste0("package:", pkg))
nsInfo <- readRDS(system.file("Meta/nsInfo.rds", package = pkg))
if (!is.null(nsInfo$S3methods)) {
S3methods <- with(nsInfo, paste(S3methods[,1], S3methods[,2], sep = "."))
} else
S3methods <- NULL
locals <- setdiff(allnames, c(exports, S3methods))
used <- character()
newones <- c(exports, S3methods)
while (length(newones)) {
mentioned <- unique(unlist(lapply(newones, function(n) {
fun <- get(n, envir = ns)
if (is.function(fun))
codetools::findGlobals(fun)
})))
used <- c(used, newones)
newones <- setdiff(intersect(mentioned, locals), used)
}
unused <- setdiff(locals, used)
unused
This still isn't quite right, but it should get you started. Some problems:
assign()
, or fiddling
with environments, etc.