Skip to content

Commit 7c485d4

Browse files
Merge fe90657 into 191e3b7
2 parents 191e3b7 + fe90657 commit 7c485d4

16 files changed

+136
-61
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,5 @@ importFrom(utils,relist)
157157
importFrom(utils,tail)
158158
importFrom(xml2,as_list)
159159
importFrom(xml2,xml_find_all)
160+
importFrom(xml2,xml_find_first)
161+
importFrom(xml2,xml_text)

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
## New and improved features
99

1010
* `library_call_linter()` can detect if all library calls are not at the top of your script (#2027, @nicholas-masel).
11+
* Linters with logic around the magrittr pipe `%>%` consistently apply it to the other pipes `%!>%`, `%T>%`, `%<>%` (and possibly `%$%`) where appropriate (#2008, @MichaelChirico).
12+
+ `brace_linter()`
13+
+ `pipe_call_linter()`
14+
+ `pipe_continuation_linter()`
15+
+ `unnecessary_concatenation_linter()`
16+
+ `unnecessary_placeholder_linter()`
1117
* Several linters avoiding false positives in `$` extractions get the same exceptions for `@` extractions, e.g. `S4@T` will no longer throw a `T_and_F_symbol_linter()` hit (#2039, @MichaelChirico).
1218
+ `T_and_F_symbol_linter()`
1319
+ `for_loop_index_linter()`

R/brace_linter.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ brace_linter <- function(allow_single_line = FALSE) {
6565
#
6666
# note that '{' is not supported in RHS call of base-R's native pipe (`|>`),
6767
# so no exception needs to be made for this operator
68-
"not(
68+
glue("not(
6969
@line1 > parent::expr/preceding-sibling::*[not(self::COMMENT)][1][
7070
self::OP-LEFT-PAREN
7171
or self::OP-COMMA
72-
or (self::SPECIAL and text() = '%>%')
72+
or (self::SPECIAL and ({xp_text_in_table(magrittr_pipes)}) )
7373
]/@line2
74-
)"
74+
)")
7575
))
7676

7777
# TODO (AshesITR): if c_style_braces is TRUE, invert the preceding-sibling condition

R/lintr-package.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#' @importFrom rex rex regex re_matches re_substitutes character_class
1313
#' @importFrom stats na.omit
1414
#' @importFrom utils capture.output head getParseData relist
15-
#' @importFrom xml2 xml_find_all as_list
15+
#' @importFrom xml2 xml_find_all xml_find_first xml_text as_list
1616
#' @importFrom cyclocomp cyclocomp
1717
#' @importFrom utils tail
1818
#' @rawNamespace

R/pipe_call_linter.R

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ pipe_call_linter <- function() {
2323
# NB: the text() here shows up as %&gt;% but that's not matched, %>% is
2424
# NB: use *[1][self::SYMBOL] to ensure the first element is SYMBOL, otherwise
2525
# we include expressions like x %>% .$col
26-
xpath <- "//SPECIAL[text() = '%>%']/following-sibling::expr[*[1][self::SYMBOL]]"
26+
pipes <- setdiff(magrittr_pipes, "%$%")
27+
xpath <- glue("//SPECIAL[{ xp_text_in_table(pipes) }]/following-sibling::expr[*[1][self::SYMBOL]]")
2728

2829
Linter(function(source_expression) {
2930
if (!is_lint_level(source_expression, "expression")) {
@@ -33,12 +34,16 @@ pipe_call_linter <- function() {
3334
xml <- source_expression$xml_parsed_content
3435

3536
bad_expr <- xml2::xml_find_all(xml, xpath)
37+
pipe <- xml_text(xml_find_first(bad_expr, "preceding-sibling::SPECIAL[1]"))
3638

3739
xml_nodes_to_lints(
3840
bad_expr,
3941
source_expression = source_expression,
40-
lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.",
42+
lint_message =
43+
sprintf("Use explicit calls in magrittr pipes, i.e., `a %1$s foo` should be `a %1$s foo()`.", pipe),
4144
type = "warning"
4245
)
4346
})
4447
}
48+
49+
magrittr_pipes <- c("%>%", "%!>%", "%T>%", "%$%", "%<>%")

R/pipe_continuation_linter.R

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ pipe_continuation_linter <- function() {
5353
# Where a single-line pipeline is nested inside a larger expression
5454
# e.g. inside a function definition), the outer expression can span multiple lines
5555
# without throwing a lint.
56-
preceding_pipe <- "preceding-sibling::expr[1]/descendant::*[self::SPECIAL[text() = '%>%'] or self::PIPE]"
57-
xpath <- glue::glue("
58-
(//PIPE | //SPECIAL[text() = '%>%'])[
56+
pipe_node <- glue("SPECIAL[{ xp_text_in_table(magrittr_pipes) }]")
57+
preceding_pipe <- glue("preceding-sibling::expr[1]/descendant::*[self::{pipe_node} or self::PIPE]")
58+
xpath <- glue("
59+
(//PIPE | //{pipe_node})[
5960
parent::expr[@line1 < @line2]
6061
and {preceding_pipe}
6162
and (
@@ -73,7 +74,7 @@ pipe_continuation_linter <- function() {
7374
xml <- source_expression$full_xml_parsed_content
7475

7576
pipe_exprs <- xml2::xml_find_all(xml, xpath)
76-
pipe_text <- ifelse(xml2::xml_name(pipe_exprs) == "PIPE", "|>", "%>%")
77+
pipe_text <- xml_text(pipe_exprs)
7778

7879
xml_nodes_to_lints(
7980
pipe_exprs,

R/unnecessary_concatenation_linter.R

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ unnecessary_concatenation_linter <- function(allow_single_expression = TRUE) { #
6767

6868
non_constant_cond <- "SYMBOL or (expr and not(OP-COLON and count(expr[SYMBOL or expr]) != 2))"
6969

70-
to_pipe_xpath <- "
70+
pipes <- setdiff(magrittr_pipes, "%$%")
71+
to_pipe_xpath <- glue("
7172
./preceding-sibling::*[1][
7273
self::PIPE or
73-
self::SPECIAL[text() = '%>%']
74+
self::SPECIAL[{ xp_text_in_table(pipes) }]
7475
]
75-
"
76+
")
7677
if (allow_single_expression) {
7778
zero_arg_cond <-
7879
glue::glue("count(expr) = 1 and not( {to_pipe_xpath} / preceding-sibling::expr[ {non_constant_cond} ])")

R/unnecessary_placeholder_linter.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
#' @export
3535
unnecessary_placeholder_linter <- function() {
3636
# TODO(michaelchirico): handle R4.2.0 native placeholder _ as well
37-
xpath <- "
38-
//SPECIAL[text() = '%>%' or text() = '%T>%' or text() = '%<>%']
37+
xpath <- glue("
38+
//SPECIAL[{ xp_text_in_table(magrittr_pipes) }]
3939
/following-sibling::expr[
4040
expr/SYMBOL_FUNCTION_CALL
4141
and not(expr[
@@ -47,7 +47,7 @@ unnecessary_placeholder_linter <- function() {
4747
SYMBOL[text() = '.']
4848
and not(preceding-sibling::*[1][self::EQ_SUB])
4949
]
50-
"
50+
")
5151

5252
Linter(function(source_expression) {
5353
if (!is_lint_level(source_expression, "expression")) {

R/yoda_test_linter.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ yoda_test_linter <- function() {
4545
or (STR_CONST and not(OP-DOLLAR or OP-AT))
4646
or ((OP-PLUS or OP-MINUS) and count(expr[NUM_CONST]) = 2)
4747
"
48-
xpath <- glue::glue("
48+
pipes <- setdiff(magrittr_pipes, c("%$%", "%<>%"))
49+
xpath <- glue("
4950
//SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical' or text() = 'expect_setequal']
5051
/parent::expr
5152
/following-sibling::expr[1][ {const_condition} ]
52-
/parent::expr[not(preceding-sibling::*[self::PIPE or self::SPECIAL[text() = '%>%']])]
53+
/parent::expr[not(preceding-sibling::*[self::PIPE or self::SPECIAL[{ xp_text_in_table(pipes) }]])]
5354
")
5455

5556
second_const_xpath <- glue::glue("expr[position() = 3 and ({const_condition})]")

tests/testthat/helper.R

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,16 @@ skip_if_not_r_version <- function(min_version) {
6161
skip_if_not_utf8_locale <- function() {
6262
testthat::skip_if_not(l10n_info()[["UTF-8"]], "Not a UTF-8 locale")
6363
}
64+
65+
pipes <- function(exclude = NULL) {
66+
if (getRversion() < "4.1.0") exclude <- unique(c(exclude, "|>"))
67+
all_pipes <- c(
68+
standard = "%>%",
69+
greedy = "%!>%",
70+
tee = "%T>%",
71+
assignment = "%<>%",
72+
extraction = "%$%",
73+
native = "|>"
74+
)
75+
all_pipes[!all_pipes %in% exclude]
76+
}

0 commit comments

Comments
 (0)