|
3 | 3 | #' Check that closures have the proper usage using [codetools::checkUsage()].
|
4 | 4 | #' Note that this runs [base::eval()] on the code, so **do not use with untrusted code**.
|
5 | 5 | #'
|
6 |
| -#' @param interpret_glue If `TRUE`, interpret [glue::glue()] calls to avoid false positives caused by local variables |
7 |
| -#' which are only used in a glue expression. |
| 6 | +#' @param interpret_glue (Deprecated) If `TRUE`, interpret [glue::glue()] calls to avoid |
| 7 | +#' false positives caused by local variables which are only used in a glue expression. |
| 8 | +#' Provide `interpret_extensions` instead, see below. |
| 9 | +#' @param interpret_extensions Character vector of extensions to interpret. These are meant to cover known cases where |
| 10 | +#' variables may be used in ways understood by the reader but not by `checkUsage()` to avoid false positives. |
| 11 | +#' Currently `"glue"` and `"rlang"` are supported, both of which are in the default. |
| 12 | +#' - For `glue`, examine [glue::glue()] calls. |
| 13 | +#' - For `rlang`, examine `.env$key` usages. |
8 | 14 | #' @param skip_with A logical. If `TRUE` (default), code in `with()` expressions
|
9 | 15 | #' will be skipped. This argument will be passed to `skipWith` argument of
|
10 | 16 | #' `codetools::checkUsage()`.
|
|
29 | 35 | #' @evalRd rd_linters("package_development")
|
30 | 36 | #' @seealso [linters] for a complete list of linters available in lintr.
|
31 | 37 | #' @export
|
32 |
| -object_usage_linter <- function(interpret_glue = TRUE, skip_with = TRUE) { |
| 38 | +object_usage_linter <- function(interpret_glue = NULL, interpret_extensions = c("glue", "rlang"), skip_with = TRUE) { |
| 39 | + if (!is.null(interpret_glue)) { |
| 40 | + lintr_deprecated( |
| 41 | + "interpret_glue", |
| 42 | + '"glue" in interpret_extensions', |
| 43 | + version = "3.3.0", |
| 44 | + type = "Argument", |
| 45 | + signal = "warning" |
| 46 | + ) |
| 47 | + |
| 48 | + if (interpret_glue) { |
| 49 | + interpret_extensions <- union(interpret_extensions, "glue") |
| 50 | + } else { |
| 51 | + interpret_extensions <- setdiff(interpret_extensions, "glue") |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + if (length(interpret_extensions) > 0L) { |
| 56 | + interpret_extensions <- match.arg(interpret_extensions, several.ok = TRUE) |
| 57 | + } |
| 58 | + |
33 | 59 | # NB: difference across R versions in how EQ_ASSIGN is represented in the AST
|
34 | 60 | # (under <expr_or_assign_or_help> or <equal_assign>)
|
35 | 61 | # NB: the repeated expr[2][FUNCTION] XPath has no performance impact, so the different direct assignment XPaths are
|
@@ -77,7 +103,7 @@ object_usage_linter <- function(interpret_glue = TRUE, skip_with = TRUE) {
|
77 | 103 | if (inherits(fun, "try-error")) {
|
78 | 104 | return()
|
79 | 105 | }
|
80 |
| - known_used_symbols <- extract_glued_symbols(fun_assignment, interpret_glue = interpret_glue) |
| 106 | + known_used_symbols <- known_used_symbols(fun_assignment, interpret_extensions = interpret_extensions) |
81 | 107 | res <- parse_check_usage(
|
82 | 108 | fun,
|
83 | 109 | known_used_symbols = known_used_symbols,
|
@@ -261,3 +287,27 @@ get_imported_symbols <- function(xml) {
|
261 | 287 | )
|
262 | 288 | }))
|
263 | 289 | }
|
| 290 | + |
| 291 | +known_used_symbols <- function(fun_assignment, interpret_extensions) { |
| 292 | + unique(c( |
| 293 | + if ("rlang" %in% interpret_extensions) extract_env_symbols(fun_assignment), |
| 294 | + if ("glue" %in% interpret_extensions) extract_glued_symbols(fun_assignment) |
| 295 | + )) |
| 296 | +} |
| 297 | + |
| 298 | +extract_env_symbols <- function(fun_assignment) { |
| 299 | + env_names <- xml_find_all( |
| 300 | + fun_assignment, |
| 301 | + " |
| 302 | + .//SYMBOL[text() = '.env'] |
| 303 | + /parent::expr |
| 304 | + /following-sibling::OP-DOLLAR |
| 305 | + /following-sibling::SYMBOL |
| 306 | + | .//SYMBOL[text() = '.env'] |
| 307 | + /parent::expr |
| 308 | + /following-sibling::LBB |
| 309 | + /following-sibling::expr[STR_CONST] |
| 310 | + " |
| 311 | + ) |
| 312 | + get_r_string(env_names) |
| 313 | +} |
0 commit comments