Skip to content

Commit f0b5762

Browse files
Merge 9adbbe8 into b5e09d0
2 parents b5e09d0 + 9adbbe8 commit f0b5762

18 files changed

+176
-16
lines changed

NEWS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@
3535
+ `undesirable_function_linter()`
3636
+ `unreachable_code_linter()`
3737
+ `yoda_test_linter()`
38+
* Linters with logic around function declarations consistently include the R 4.0.0 shorthand `\()` (#2190, @MichaelChirico).
39+
+ `brace_linter()`
40+
+ `function_left_parentheses_linter()`
41+
+ `indentation_linter()`
42+
+ `object_length_linter()`
43+
+ `object_name_linter()`
44+
+ `package_hooks_linter()`
45+
+ `paren_body_linter()`
46+
+ `unnecessary_lambda_linter()`
47+
+ `unreachable_code_linter()`
3848
* `sprintf_linter()` is pipe-aware, so that `x %>% sprintf(fmt = "%s")` no longer lints (#1943, @MichaelChirico).
3949
* `line_length_linter()` helpfully includes the line length in the lint message (#2057, @MichaelChirico).
4050
* `conjunct_test_linter()` also lints usage like `dplyr::filter(x, A & B)` in favor of using `dplyr::filter(x, A, B)` (part of #884; #2110 and #2078, @salim-b and @MichaelChirico). Option `allow_filter` toggles when this applies. `allow_filter = "always"` drops such lints entirely, while `"not_dplyr"` only lints calls explicitly qualified as `dplyr::filter()`. The default, `"never"`, assumes all unqualified calls to `filter()` are `dplyr::filter()`.

R/brace_linter.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ brace_linter <- function(allow_single_line = FALSE) {
124124
# TODO (AshesITR): if c_style_braces is TRUE, this needs to be @line2 + 1
125125
xp_else_same_line <- glue("//ELSE[{xp_else_closed_curly} and @line1 != {xp_else_closed_curly}/@line2]")
126126

127-
xp_function_brace <- "//FUNCTION/parent::expr[@line1 != @line2 and not(expr[OP-LEFT-BRACE])]"
127+
xp_function_brace <- "(//FUNCTION | //OP-LAMBDA)/parent::expr[@line1 != @line2 and not(expr[OP-LEFT-BRACE])]"
128128

129129
# if (x) { ... } else if (y) { ... } else { ... } is OK; fully exact pairing
130130
# of if/else would require this to be

R/declared_functions.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ declared_s3_generics <- function(x) {
55

66
# Assigns to a symbol
77
"[./LEFT_ASSIGN|EQ_ASSIGN]",
8-
"[./expr[FUNCTION]]",
8+
"[./expr[FUNCTION or OP-LAMBDA]]",
99
"[./expr/SYMBOL]",
1010

1111
# Is a S3 Generic (contains call to UseMethod)

R/function_left_parentheses_linter.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ function_left_parentheses_linter <- function() { # nolint: object_length.
4646
# complicated call to an "extracted" function (see #1963). This mistake was made earlier
4747
# because it allows the xpath to be the same for both FUNCTION and SYMBOL_FUNCTION_CALL.
4848
# Further, write 4 separate XPaths because the 'range_end_xpath' differs for these two nodes.
49-
bad_line_fun_xpath <- "//FUNCTION[@line1 != following-sibling::OP-LEFT-PAREN/@line1]"
49+
bad_line_fun_xpath <- "(//FUNCTION | //OP-LAMBDA)[@line1 != following-sibling::OP-LEFT-PAREN/@line1]"
5050
bad_line_call_xpath <- "//SYMBOL_FUNCTION_CALL[@line1 != parent::expr/following-sibling::OP-LEFT-PAREN/@line1]"
51-
bad_col_fun_xpath <- "//FUNCTION[
51+
bad_col_fun_xpath <- "(//FUNCTION | //OP-LAMBDA)[
5252
@line1 = following-sibling::OP-LEFT-PAREN/@line1
5353
and @col2 != following-sibling::OP-LEFT-PAREN/@col1 - 1
5454
]"

R/indentation_linter.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ indentation_linter <- function(indent = 2L, hanging_indent_style = c("tidy", "al
124124
paren_tokens_right <- c("OP-RIGHT-BRACE", "OP-RIGHT-PAREN", "OP-RIGHT-BRACKET", "OP-RIGHT-BRACKET")
125125
infix_tokens <- setdiff(infix_metadata$xml_tag, c("OP-LEFT-BRACE", "OP-COMMA", paren_tokens_left))
126126
no_paren_keywords <- c("ELSE", "REPEAT")
127-
keyword_tokens <- c("FUNCTION", "IF", "FOR", "WHILE")
127+
keyword_tokens <- c("FUNCTION", "OP-LAMBDA", "IF", "FOR", "WHILE")
128128

129129
xp_last_on_line <- "@line1 != following-sibling::*[not(self::COMMENT)][1]/@line1"
130130

@@ -342,7 +342,7 @@ build_indentation_style_tidy <- function() {
342342
#> body
343343
#> }
344344
xp_is_double_indent <- "
345-
parent::expr[FUNCTION and not(@line1 = SYMBOL_FORMALS/@line1)]
345+
parent::expr[(FUNCTION or OP-LAMBDA) and not(@line1 = SYMBOL_FORMALS/@line1)]
346346
/OP-RIGHT-PAREN[@line1 = preceding-sibling::*[not(self::COMMENT)][1]/@line2]
347347
"
348348

R/package_hooks_linter.R

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ package_hooks_linter <- function() {
5454
.onAttach = c(bad_msg_calls, "library.dynam")
5555
)
5656
bad_msg_call_xpath_fmt <- "
57-
//FUNCTION
57+
(//FUNCTION | //OP-LAMBDA)
5858
/parent::expr[preceding-sibling::expr/SYMBOL[text() = '%s']]
5959
//SYMBOL_FUNCTION_CALL[%s]
6060
"
@@ -81,7 +81,7 @@ package_hooks_linter <- function() {
8181
hook_xpath <- sprintf("string(./ancestor::expr/expr/SYMBOL[%s])", ns_calls)
8282

8383
load_arg_name_xpath <- "
84-
//FUNCTION
84+
(//FUNCTION | //OP-LAMBDA)
8585
/parent::expr[
8686
preceding-sibling::expr/SYMBOL[text() = '.onAttach' or text() = '.onLoad']
8787
and (
@@ -95,7 +95,7 @@ package_hooks_linter <- function() {
9595
"
9696

9797
library_require_xpath <- "
98-
//FUNCTION
98+
(//FUNCTION | //OP-LAMBDA)
9999
/parent::expr[preceding-sibling::expr/SYMBOL[text() = '.onAttach' or text() = '.onLoad']]
100100
//*[1][
101101
(self::SYMBOL or self::SYMBOL_FUNCTION_CALL)
@@ -104,13 +104,13 @@ package_hooks_linter <- function() {
104104
"
105105

106106
bad_unload_call_xpath <- "
107-
//FUNCTION
107+
(//FUNCTION | //OP-LAMBDA)
108108
/parent::expr[preceding-sibling::expr/SYMBOL[text() = '.Last.lib' or text() = '.onDetach']]
109109
//SYMBOL_FUNCTION_CALL[text() = 'library.dynam.unload']
110110
"
111111

112112
unload_arg_name_xpath <- "
113-
//FUNCTION
113+
(//FUNCTION | //OP-LAMBDA)
114114
/parent::expr[
115115
preceding-sibling::expr/SYMBOL[text() = '.onDetach' or text() = '.Last.lib']
116116
and (

R/paren_body_linter.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ paren_body_linter <- make_linter_from_xpath(
3333
and @line1 = following-sibling::expr[1]/@line1
3434
and (
3535
preceding-sibling::FUNCTION
36+
or preceding-sibling::OP-LAMBDA
3637
or preceding-sibling::IF
3738
or preceding-sibling::WHILE
3839
or preceding-sibling::OP-LAMBDA

R/unnecessary_lambda_linter.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ unnecessary_lambda_linter <- function() {
6464
//SYMBOL_FUNCTION_CALL[ {apply_funs} ]
6565
/parent::expr
6666
/following-sibling::expr[
67-
FUNCTION
67+
(FUNCTION or OP-LAMBDA)
6868
and count(SYMBOL_FORMALS) = 1
6969
and {paren_path}/OP-LEFT-PAREN/following-sibling::expr[1][not(preceding-sibling::*[1][self::EQ_SUB])]/SYMBOL
7070
and SYMBOL_FORMALS = {paren_path}/OP-LEFT-PAREN/following-sibling::expr[1]/SYMBOL

R/unreachable_code_linter.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
unreachable_code_linter <- function() {
5858
# NB: use not(OP-DOLLAR) to prevent matching process$stop(), #1051
5959
xpath_return_stop <- "
60-
//FUNCTION
60+
(//FUNCTION | //OP-LAMBDA)
6161
/following-sibling::expr
6262
/expr[expr[1][not(OP-DOLLAR or OP-AT) and SYMBOL_FUNCTION_CALL[text() = 'return' or text() = 'stop']]]
6363
/following-sibling::*[

tests/testthat/test-brace_linter.R

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ test_that("brace_linter lints braces correctly", {
22
open_curly_msg <- rex::rex(
33
"Opening curly braces should never go on their own line"
44
)
5-
closed_curly_msg <- rex::rex(paste(
6-
"Closing curly-braces should always be on their own line,",
5+
closed_curly_msg <- rex::rex(
6+
"Closing curly-braces should always be on their own line, ",
77
"unless they are followed by an else."
8-
))
8+
)
99

1010
linter <- brace_linter()
1111
expect_lint("blah", NULL, linter)
@@ -553,3 +553,21 @@ test_that("code with pipes is handled correctly", {
553553
linter
554554
)
555555
})
556+
557+
test_that("function shorthand is treated like 'full' function", {
558+
skip_if_not_r_version("4.1.0")
559+
linter <- brace_linter()
560+
561+
expect_lint("a <- \\() { \n}", NULL, linter)
562+
expect_lint(
563+
trim_some("
564+
x <- \\()
565+
{2}
566+
"),
567+
list(
568+
rex::rex("Opening curly braces should never go on their own line"),
569+
rex::rex("Closing curly-braces should always be on their own line")
570+
),
571+
linter
572+
)
573+
})

0 commit comments

Comments
 (0)