help("walkAST")
.packagesOf()
for Globals
failed to return the package of the
globals if the global doesn't have a closure, e.g. base::pi
and data.table::.N
.[[<-
and [<-
for Globals
, to complement $<-
.Globals
object guarantee that the
where
and the class
attributes are always the last two
attributes and in that order.c()
for Globals
would lose the where
environment for any
functions appended.cleanup()
assumed it was safe to call env$.packageName
on 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 argument locals
, which controls whether
globals that exist in "local" environments of a function should be
considered or not, e.g. in f <- local({ a <- 1; function() a })
,
should a
be considered a global of f()
or not. For backward
compatibility reasons, the default is locals = TRUE
, but this
might become locals = FALSE
in a later release.
Any globals.*
options specific to this packages can now be set
via environment variables R_GLOBALS_*
when the package is loaded.
For example, R_GLOBALS_DEBUG=true
sets option globals.debug = TRUE
.
as.Globals(list(a = NULL))
and c(Globals(), list(a = NULL))
would include the calling environment instead of an empty
environment as part of the where
attribute.Now findGlobals(function(x) x <- x)
identifies x
as a global
variable.
Now findGlobals(function(x) x[1] <- 0)
identifies x
as a global
variable. Same for other variants like x[[1]] <- 0
and x$a <- 0
.
Now findGlobals(function(z) x <- z$x)
identifies x
as a global
variable.
Now findGlobals(quote({ f <- function(x) x; x }))
identifies x
as a global variable. Previously, the x
of the function would
hide the global x
.
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 argument envir
has attributes set.
findGlobals()
works around a bug in stats:::[.formula
of R (<
4.1.0) that revealed itself when scanning formulas with NULL
components.
findGlobals()
would not pass down argument dotdotdot
when
recursively parsing assignments.
findGlobals()
could return ...
as a global also when used in
formulas. Now it respects argument dotdotdot = "ignore"
and
parses formulas accordingly, otherwise formulas will be parsed
using dotdotdot = "return"
.
findGlobals(expr)
now also scans any attributes of expr
for
globals, e.g. purrr::partial()
puts the original function in
attribute body
. Argument attributes
controls which attributes,
if any, should be scanned. Default is to scan all attributes.
findGlobals()
, globalsOf()
, and globalsByName()
now
recognizes and returns values for ..1
, ..2
, etc. like they do
for ...
.
cleanup()
now also drop exported and non-exported
NativeSymbolInfo
objects.
cleanup()
gained support for dropping NativeSymbolInfo
objects.findGlobals()
did not pass down argument method
in 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"))
for Globals
would
drop base R objects with names not exported by the corresponding
base R package. Similarly, drop = c(..., "primitive")
would drop
primitive R objects with names not exported by any base R package.
findGlobals()
, globalsOf()
, and globalsByName()
did not
handle ..1
, ..2
, etc.
findGlobals()
and globalsOf()
produces warnings on
'...
, ..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 identify
a
as 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 internal envname(env)
did not always handle when
class(env) != "environment"
.findGlobals()
, globalsOf()
, and packagesOf()
no longer
return elements sorted by name.findGlobals()
would not identify a
as a global in
expressions of type a[1] = ...
and names(a) = ...
although it
did for a[1] <- ...
and names(a) <- ...
.cleanup()
for Globals
should 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.globals::findGlobals()
.globals::findGlobals(x)
, where x
is a list, iterated over x
incorrectly assuming no method dispatching on x
would take
place. For instance, if x
contained an fst::fst_table
object,
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).
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 type cannot contain globals
and can therefore be skipped early in the search for globals.findGlobals()
identifies a
as a global also when
it is part of LHS expressions of type a[1] <- ...
and names(a) <- ...
.globals::findGlobals()
incorrectly identified a
as a global in
expression of type a <- pkg::a
.
If ...
was passed to globalsByName(names)
, an error would be
produced unless it was the last entry in names
.
findGlobals()
identifies x
as a global variable in
x <- x + 1
and likewise for x + 1 -> x
. Note that ditto
using <<-
and ->>
was already identifying x
as 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 type environment
.walkAST()
could produce error "Cannot walk expression. Unknown
object type '...'" for objects of type list
, expression
and
S4
.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:
walkAST()
could produce error "Cannot walk expression. Unknown
object type 'nnn'" for expressions of type builtin
, closure
and special
.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 a local()
statement.
globalsOf()
identifies also globals in locally defined
functions. This can be disabled with argument recursive = FALSE
.
findGlobals()
now takes both closures (functions) and
expressions.
c(x, list())
where x
is a Globals
object would give an error
reporting that the list does not have named elements.Globals()
and as.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()
for Globals
objects.
Improved as.Globals()
for lists.
globalsOf(..., mustExist = TRUE)
when
it fails to locate a global also gives information on the
expression that is problematic.cleanup()
for Globals
did not cleanup functions in core package
environments named package:<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 }
. Now b
is correctly identified as a global
object. Previously it would have been missed. For backward
compatibility, the previous behavior can be obtained using
argument method = "conservative"
.globalsOf()
now returns attribute where
specifying 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()
and globalsOf()
gained argument dotdotdot
.getGlobals()
to globalsOf()
.Added [()
for Globals
.
findGlobals()
and getGlobals()
gained argument substitute
.
Added cleanup(..., method = "internals")
.
Added Globals
class with methods cleanup()
and packagesOf()
.
Added as.Globals()
to coerce lists to Globals
objects.
getGlobals()
gained argument mustExist
for 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()
and getGlobals()
gained argument method
for
controlling whether a "conservative"
or a "liberal"
algorithm
for identifying true globals should be used.