- ...
- Globals objects could not be sub-assigned by index - only by name,
e.g. with
x <- as.Globals(list(a = 1)),x[[1]] <- 2would give an error.
findGlobals()is now much faster whenexprcontains a list of classed lists, e.g. a list of data frames.
-
findGlobals(function(x = a) NULL, method = "dfs")failed to identifyaas global variable. -
findGlobals(function(...) ..1, method = "dfs")incorrectly identified..1as a global variable.
-
Add
findGlobals(..., method = "dfs"), which finds globals in R expressions by walking its abstract syntax tree (AST) using depth-first search. This new approach does a better job in emulating how the R engine identifies global variables. For example, the new"dfs"method picks upxinlocal({ function(x) x; x })as a global variable, which the"ordered"method fails to do. Analogously,globalsOf()gained support formethod = "dfs" -
Now
findGlobals()supportsexpressionobjects, e.g.findGlobals(expression(x + y)). -
It is now possible to specify multiple
methodsearch algorithms forfindGlobals()andglobalsOf(), which then will combine the results from all of them, e.g.findGlobals(expr, method = c("dfs", "ordered")).
walkAST()did not recognize objects of typeexternalptr, leading to an error onCannot walk expression. Unknown object type 'externalptr'.
walkAST()now also walks the body of closures ("functions").
-
walkAST()did not recognize objects of typeobject, leading to an error onCannot walk expression. Unknown object type 'object'. -
findGlobals()would produceError in e[[4]] : subscript out of expressions of formatfor expressions of typeLHS INFIX_OP `$<-`(name, value), e.g.x %>% `$<-`("a", 42). This is due to a bug in the codetools package, whichfindGlobals()now works around internally.
-
globalsByName(), and therefore alsoglobalsOf(), did not support special arguments..1,..2, etc. -
cleanup(globals, drop)on aGlobalsobject with non-existing globals and wheredropdid not specify"missing"would throw anError in exists(name, envir = env) : use of NULL environment is defunct. Now the non-existing ("missing") globals are preserved.
- Drop duplicated arguments from
help("walkAST").
packagesOf()forGlobalsfailed to return the package of the globals if the global doesn't have a closure, e.g.base::pianddata.table::.N.
- Add
[[<-and[<-forGlobals, to complement$<-.
- All functions modifying a
Globalsobject guarantee that thewhereand theclassattributes are always the last two attributes and in that order.
c()forGlobalswould lose thewhereenvironment for any functions appended.
cleanup()assumed it was safe to callenv$.packageNameon each scanned environment, but that might not be true. A classed environment could be such that$()gives an error, rather than returning something.
-
globalsOf()gained argumentlocals, which controls whether globals that exist in "local" environments of a function should be considered or not, e.g. inf <- local({ a <- 1; function() a }), shouldabe considered a global off()or not. For backward compatibility reasons, the default islocals = TRUE, but this might becomelocals = FALSEin a later release. -
Any
globals.*options specific to this package can now be set via environment variablesR_GLOBALS_*when the package is loaded. For example,R_GLOBALS_DEBUG=truesets optionglobals.debug = TRUE.
as.Globals(list(a = NULL))andc(Globals(), list(a = NULL))would include the calling environment instead of an empty environment as part of thewhereattribute.
-
Now
findGlobals(function(x) x <- x)identifiesxas a global variable. -
Now
findGlobals(function(x) x[1] <- 0)identifiesxas a global variable. Same for other variants likex[[1]] <- 0andx$a <- 0. -
Now
findGlobals(function(z) x <- z$x)identifiesxas a global variable. -
Now
findGlobals(quote({ f <- function(x) x; x }))identifiesxas a global variable. Previously, thexof the function would hide the globalx.
-
globalsOf()could produce "Error in vapply(where, FUN = envname, FUN.VALUE = NA_character_, USE.NAMES = FALSE) : values must be length 1, but FUN(X[[2]]) result is length 10". This would happen if for instance argumentenvirhas attributes set. -
findGlobals()works around a bug instats:::[.formulaof R (< 4.1.0) that revealed itself when scanning formulas with NULL components. -
findGlobals()would not pass down argumentdotdotdotwhen recursively parsing assignments. -
findGlobals()could return...as a global also when used in formulas. Now it respects argumentdotdotdot = "ignore"and parses formulas accordingly, otherwise formulas will be parsed usingdotdotdot = "return".
-
findGlobals(expr)now also scans any attributes ofexprfor globals, e.g.purrr::partial()puts the original function in attributebody. Argumentattributescontrols which attributes, if any, should be scanned. Default is to scan all attributes. -
findGlobals(),globalsOf(), andglobalsByName()now recognize and return values for..1,..2, etc. like they do for.... -
cleanup()now also drops exported and non-exportedNativeSymbolInfoobjects.
cleanup()gained support for droppingNativeSymbolInfoobjects.
-
findGlobals()did not pass down argumentmethodin recursive calls. -
findGlobals(expr)would fail to identify globals in anonymous function calls, e.g.expr <- as.call(list(function(...) NOT_FOUND, quote(FOUND))). -
Calls like
findGlobals(~ NULL)with NULLs on the right-hand side could throw "Error in if (length(ans) == 0L || as.character(ans[[1L]])[1L] == "~") { : missing value where TRUE/FALSE needed". Solved by working around what looks like a bug in the stats package causing subsetting on formulas with NULLs to fail. -
cleanup(..., drop = c(..., "base-packages"))forGlobalswould drop base R objects with names not exported by the corresponding base R package. Similarly,drop = c(..., "primitives")would drop primitive R objects with names not exported by any base R package. -
findGlobals(),globalsOf(), andglobalsByName()did not handle..1,..2, etc. -
findGlobals()andglobalsOf()produces warnings on ': ... may be used in an incorrect context' when formulas had...,..1,..2, etc. -
findGlobals(function() NULL, substitute = TRUE, trace = TRUE)would throw "Error in environment(w$enterLocal) : object 'w' not found".
findGlobals(function() { a; a <- a + 1 })would fail to identifyaas a global variable whereas it was properly identified with{ a <- a + 1; a }.
globalsOf()could produce "Error in vapply(where, FUN = envname, FUN.VALUE = NA_character_, USE.NAMES = FALSE) : values must be length 1, but FUN(X[[...]]) result is length ...". This was because the internalenvname(env)did not always handle whenclass(env) != "environment".
findGlobals(),globalsOf(), andpackagesOf()no longer return elements sorted by name.
- globals::
findGlobals()would not identifyaas a global in expressions of typea[1] = ...andnames(a) = ...although it did fora[1] <- ...andnames(a) <- ....
cleanup()forGlobalsshould now be much faster. Previously, it could be very slow the first time it was called in a fresh R session, especially if the user had a large number of packages installed and/or the package libraries were on slow drives.
- Added help for
globals::findGlobals().
-
globals::findGlobals(x), wherexis a list, iterated overxincorrectly assuming no method dispatching onxwould take place. For instance, ifxcontained anfst::fst_tableobject, then "Error in .subset2(x, i, exact = exact) : subscript out of bounds" would be produced. -
globals::
findGlobals()could produce a "Warning in is.na(x): is.na() applied to non-(list or vector) of type 'NULL'" in R (< 3.5.0).
- globals::
findGlobals()is now significantly faster for elements that are long lists with many elements of basic data types. This is because elements of such basic data types cannot contain globals and can therefore be skipped early in the search for globals.
- Now globals::
findGlobals()identifiesaas a global also when it is part of LHS expressions of typea[1] <- ...andnames(a) <- ....
-
globals::
findGlobals()incorrectly identifiedaas a global in expression of typea <- pkg::a. -
If
...was passed toglobalsByName(names), an error would be produced unless it was the last entry innames.
- Now
findGlobals()identifiesxas a global variable inx <- x + 1and likewise forx + 1 -> x. Note that ditto using<<-and->>was already identifyingxas a global.
findGlobals(..., trace = TRUE)now outputs only to standard error. Previously, some of the output went to standard output.
globalsOf(..., recursive = TRUE)would result in "Error in match.fun(FUN) : node stack overflow" if one of the globals identified was a function that called itself recursively (either directly or indirectly).
walkAST()could produce error "Cannot walk expression. Unknown object type '...'" for objects of typeenvironment.
walkAST()could produce error "Cannot walk expression. Unknown object type '...'" for objects of typelist,expressionandS4.
-
Globals that are part of a formula are now identified.
-
findGlobals(..., trace = TRUE)will now show low-level parse information as the abstract syntax tree (AST) is walked.
SOFTWARE QUALITY:
- Enabled more internal sanity checks.
walkAST()could produce error "Cannot walk expression. Unknown object type 'nnn'" for expressions of typebuiltin,closureandspecial.
- Added option
globals.debug, which when TRUE enables debugging output.
-
globalsOf(..., recursive = TRUE)would in some cases scan an incorrect subset of already identified globals. -
globalsOf(..., recursive = TRUE)failed to skip objects part of package namespaces that where defined via alocal()statement.
-
globalsOf()identifies also globals in locally defined functions. This can be disabled with argumentrecursive = FALSE. -
findGlobals()now takes both closures (functions) and expressions.
c(x, list())wherexis aGlobalsobject would give an error reporting that the list does not have named elements.
Globals()andas.Globals()now accepts an empty list as input as well.
walkAST(quote( function(x=NULL) 0 ))would give a sanity check error due to the NULL argument. Thank you GitHub user 'billy34' for reporting on this.
-
Added
walkAST(), which can be used to tweak expressions. -
Added
globalsByName()for locating and retrieving a set of known global variables. -
Added
c(),$<-(),names(),unique()forGlobalsobjects. -
Improved
as.Globals()for lists.
- Now the error message of
globalsOf(..., mustExist = TRUE)when it fails to locate a global also gives information on the expression that is problematic.
cleanup()forGlobalsdid not cleanup functions in core package environments namedpackage:<name>.
findGlobals()is updated to handle the case where a local variable is overwriting a global one with the same name, e.g.{ a <- b; b <- 1 }. Nowbis correctly identified as a global object. Previously it would have been missed. For backward compatibility, the previous behavior can be obtained using argumentmethod = "conservative".
globalsOf()now returns attributewherespecifying where each global object is located.
cleanup()now only drops objects that are located in one of the "base" packages; previously it would also drop copies of such objects, e.g.FUN <- base::sample.
globalsOf()failed to return global variables with value NULL. They were identified but silently dropped.
findGlobals()andglobalsOf()gained argumentdotdotdot.
- More test coverage.
- Renamed
getGlobals()toglobalsOf().
-
Added
[()forGlobals. -
findGlobals()andgetGlobals()gained argumentsubstitute. -
Added
cleanup(..., method = "internals").
-
Added
Globalsclass with methodscleanup()andpackagesOf(). -
Added
as.Globals()to coerce lists toGlobalsobjects.
-
getGlobals()gained argumentmustExistfor controlling whether to give an error when the corresponding object for an identified global cannot be found or to silently drop the missing global. -
findGlobals()andgetGlobals()gained argumentmethodfor controlling whether a"conservative"or a"liberal"algorithm for identifying true globals should be used.
- Moved "globals" functions from an in-house package to this package.
- Created.