Search code examples
rdevtools

Find unused/"orphaned" non-exported objects during package development


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.


Solution

  • 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:

    • it assumes no functions are doing funny stuff like assign(), or fiddling with environments, etc.
    • it doesn't detect functions that are used entirely to build other objects in the package.
    • it doesn't detect weird S3 methods declared with a non-standard name.