@@ -61,13 +61,24 @@ object_usage_linter <- function(interpret_glue = NULL, interpret_extensions = c(
6161 # NB: the repeated expr[2][FUNCTION] XPath has no performance impact, so the different direct assignment XPaths are
6262 # split for better readability, see PR#1197
6363 # TODO(#1106): use //[...] to capture assignments in more scopes
64- xpath_function_assignment <- "
65- expr[LEFT_ASSIGN or EQ_ASSIGN]/expr[2][FUNCTION or OP-LAMBDA]
66- | expr_or_assign_or_help[EQ_ASSIGN]/expr[2][FUNCTION or OP-LAMBDA]
67- | equal_assign[EQ_ASSIGN]/expr[2][FUNCTION or OP-LAMBDA]
68- | //SYMBOL_FUNCTION_CALL[text() = 'assign']/parent::expr/following-sibling::expr[2][FUNCTION or OP-LAMBDA]
69- | //SYMBOL_FUNCTION_CALL[text() = 'setMethod']/parent::expr/following-sibling::expr[3][FUNCTION or OP-LAMBDA]
70- "
64+ fun_node <- " FUNCTION or OP-LAMBDA"
65+ xpath_function_assignment <- glue("
66+ expr[LEFT_ASSIGN or EQ_ASSIGN]/expr[2][{fun_node}]
67+ | expr_or_assign_or_help[EQ_ASSIGN]/expr[2][{fun_node}]
68+ | equal_assign[EQ_ASSIGN]/expr[2][{fun_node}]
69+ | //SYMBOL_FUNCTION_CALL[text() = 'assign']/parent::expr/following-sibling::expr[2][{fun_node}]
70+ | //SYMBOL_FUNCTION_CALL[text() = 'setMethod']/parent::expr/following-sibling::expr[3][{fun_node}]
71+ " )
72+
73+ # code like:
74+ # foo <- \ #comment
75+ # (x) x
76+ # is technically valid, but won't parse unless the lambda is in a bigger expression (here '<-').
77+ # the same doesn't apply to 'function', which is acknowledged as "not worth a breaking change to fix":
78+ # https://bugs.r-project.org/show_bug.cgi?id=18924. If we find such code (which has only ever
79+ # arisen in content fuzzing where we inject comments at random to the AST), we have to avoid parsing
80+ # it as a standalone expression.
81+ xpath_unsafe_lambda <- " OP-LAMBDA[@line1 = following-sibling::*[1][self::COMMENT]/@line1]"
7182
7283 # not all instances of linted symbols are potential sources for the observed violations -- see #1914
7384 symbol_exclude_cond <- " preceding-sibling::OP-DOLLAR or preceding-sibling::OP-AT or ancestor::expr[OP-TILDE]"
@@ -100,7 +111,9 @@ object_usage_linter <- function(interpret_glue = NULL, interpret_extensions = c(
100111 fun_assignments <- xml_find_all(xml , xpath_function_assignment )
101112
102113 lapply(fun_assignments , function (fun_assignment ) {
103- code <- get_content(lines = source_expression $ content , fun_assignment )
114+ # this will mess with the source line numbers. but I don't think anybody cares.
115+ needs_braces <- ! is.na(xml_find_first(fun_assignment , xpath_unsafe_lambda ))
116+ code <- get_content(lines = source_expression $ content , fun_assignment , needs_braces = needs_braces )
104117 fun <- try_silently(eval(
105118 envir = env ,
106119 parse(
@@ -190,8 +203,8 @@ get_assignment_symbols <- function(xml) {
190203 expr[RIGHT_ASSIGN]/expr[2]/SYMBOL[1] |
191204 equal_assign/expr[1]/SYMBOL[1] |
192205 expr_or_assign_or_help/expr[1]/SYMBOL[1] |
193- expr[expr[1][SYMBOL_FUNCTION_CALL/text()= 'assign']]/expr[2]/* |
194- expr[expr[1][SYMBOL_FUNCTION_CALL/text()= 'setMethod']]/expr[2]/*
206+ expr[expr[1][SYMBOL_FUNCTION_CALL/text() = 'assign']]/expr[2]/* |
207+ expr[expr[1][SYMBOL_FUNCTION_CALL/text() = 'setMethod']]/expr[2]/*
195208 "
196209 ))
197210}
0 commit comments