-
Notifications
You must be signed in to change notification settings - Fork 188
Add pipe consistency linter #2018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f0fc3a2
07f57dd
6e56008
7132a18
648ae96
e9b15f5
496cdd8
3707d99
2279132
9b42407
c5d74ce
de9d356
a27486d
bbcb9fb
6c3da23
eaff8a2
483a8e4
c702ee4
d93d9a3
c3a7fdb
f02a4b4
8a9666b
02ea7e2
1e08abd
1752df1
b21e686
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#' Pipe consistency linter | ||
#' | ||
#' Check that pipe operators are used consistently by file, or optionally | ||
#' specify one valid pipe operator. | ||
#' | ||
#' @param pipe Which pipe operator is valid (either `"%>%"` or `"|>"`). By default | ||
#' (`"auto"`), the linter has no preference but will check that each file uses | ||
#' only one type of pipe operator. | ||
#' | ||
#' @examples | ||
#' # will produce lints | ||
#' lint( | ||
#' text = "1:3 |> mean() %>% as.character()", | ||
#' linters = pipe_consistency_linter() | ||
#' ) | ||
#' | ||
#' lint( | ||
#' text = "1:3 %>% mean() %>% as.character()", | ||
#' linters = pipe_consistency_linter("|>") | ||
#' ) | ||
#' | ||
#' # okay | ||
#' lint( | ||
#' text = "1:3 %>% mean() %>% as.character()", | ||
#' linters = pipe_consistency_linter() | ||
#' ) | ||
#' | ||
#' lint( | ||
#' text = "1:3 |> mean() |> as.character()", | ||
#' linters = pipe_consistency_linter() | ||
#' ) | ||
#' @evalRd rd_tags("pipe_consistency_linter") | ||
#' @seealso [linters] for a complete list of linters available in lintr. | ||
#' @export | ||
pipe_consistency_linter <- function(pipe = c("auto", "%>%", "|>")) { | ||
pipe <- match.arg(pipe) | ||
|
||
xpath_magrittr <- glue("//SPECIAL[{ xp_text_in_table(magrittr_pipes) }]") | ||
xpath_native <- "//PIPE" | ||
|
||
Linter(function(source_expression) { | ||
if (!is_lint_level(source_expression, "file")) { | ||
return(list()) | ||
} | ||
|
||
xml <- source_expression$full_xml_parsed_content | ||
|
||
match_magrittr <- xml_find_all(xml, xpath_magrittr) | ||
match_native <- xml_find_all(xml, xpath_native) | ||
|
||
n_magrittr <- length(match_magrittr) | ||
n_native <- length(match_native) | ||
|
||
if (pipe == "auto" && n_magrittr > 0L && n_native > 0L) { | ||
xml_nodes_to_lints( | ||
xml = c(match_magrittr, match_native), | ||
source_expression = source_expression, | ||
lint_message = glue( | ||
"Found {n_magrittr} instances of %>% and {n_native} instances of |>. ", | ||
"Stick to one pipe operator." | ||
), | ||
type = "style" | ||
) | ||
} else if (pipe == "%>%" && n_native > 0L) { | ||
xml_nodes_to_lints( | ||
xml = match_native, | ||
source_expression = source_expression, | ||
lint_message = "Use the %>% pipe operator instead of the |> pipe operator.", | ||
type = "style" | ||
) | ||
} else if (pipe == "|>" && n_magrittr > 0L) { | ||
xml_nodes_to_lints( | ||
xml = match_magrittr, | ||
source_expression = source_expression, | ||
lint_message = "Use the |> pipe operator instead of the %>% pipe operator.", | ||
type = "style" | ||
) | ||
} else { | ||
list() | ||
} | ||
}) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
test_that("pipe_consistency skips allowed usage", { | ||
skip_if_not_r_version("4.1.0") | ||
linter <- pipe_consistency_linter() | ||
|
||
expect_lint("1:3 %>% mean() %>% as.character()", NULL, linter) | ||
expect_lint("1:3 |> mean() |> as.character()", NULL, linter) | ||
# With no pipes | ||
expect_lint("x <- 1:5", NULL, linter) | ||
# Across multiple lines | ||
expect_lint( | ||
trim_some(" | ||
1:3 %>% | ||
mean() %>% | ||
as.character() | ||
"), | ||
NULL, | ||
linter | ||
) | ||
}) | ||
|
||
test_that("pipe_consistency lints inconsistent usage", { | ||
skip_if_not_r_version("4.1.0") | ||
linter <- pipe_consistency_linter() | ||
expected_msg <- rex("Found 1 instances of %>% and 1 instances of |>. Stick to one pipe operator.") | ||
|
||
expect_lint( | ||
"1:3 |> mean() %>% as.character()", | ||
list( | ||
list(message = expected_msg, line_number = 1L, column_number = 5L), | ||
list(message = expected_msg, line_number = 1L, column_number = 15L) | ||
), | ||
linter | ||
) | ||
|
||
expect_lint( | ||
"1:3 %>% mean() |> as.character()", | ||
list( | ||
list(message = expected_msg, line_number = 1L, column_number = 5L), | ||
list(message = expected_msg, line_number = 1L, column_number = 16L) | ||
), | ||
linter | ||
) | ||
|
||
expect_lint( | ||
trim_some(" | ||
1:3 %>% | ||
mean() |> | ||
as.character() | ||
"), | ||
list( | ||
list(message = expected_msg, line_number = 1L, column_number = 5L), | ||
list(message = expected_msg, line_number = 2L, column_number = 10L) | ||
), | ||
linter | ||
) | ||
|
||
expected_msg_multi <- rex("Found 1 instances of %>% and 2 instances of |>. Stick to one pipe operator.") | ||
expect_lint( | ||
"1:3 |> sort() |> mean() %>% as.character()", | ||
list( | ||
list(message = expected_msg_multi, line_number = 1L, column_number = 5L), | ||
list(message = expected_msg_multi, line_number = 1L, column_number = 15L), | ||
list(message = expected_msg_multi, line_number = 1L, column_number = 25L) | ||
), | ||
linter | ||
) | ||
}) | ||
|
||
|
||
test_that("pipe_consistency_linter works with |> argument", { | ||
skip_if_not_r_version("4.1.0") | ||
|
||
linter <- pipe_consistency_linter(pipe = "|>") | ||
expected_message <- rex("Use the |> pipe operator instead of the %>% pipe operator.") | ||
|
||
expect_lint( | ||
trim_some(" | ||
1:3 %>% | ||
mean() %>% | ||
as.character() | ||
"), | ||
list( | ||
list(message = expected_message, line_number = 1L, column_number = 5L), | ||
list(message = expected_message, line_number = 2L, column_number = 10L) | ||
), | ||
linter | ||
) | ||
|
||
expect_lint( | ||
trim_some(" | ||
1:3 |> | ||
mean() %>% | ||
as.character() | ||
"), | ||
list(message = expected_message, line_number = 2L, column_number = 10L), | ||
linter | ||
) | ||
|
||
expect_lint( | ||
"1:3 |> mean() |> as.character()", | ||
NULL, | ||
linter | ||
) | ||
|
||
expect_lint( | ||
trim_some(" | ||
1:3 |> | ||
mean() %>% | ||
as.character() | ||
"), | ||
list(message = expected_message, line_number = 2L, column_number = 10L), | ||
linter | ||
) | ||
}) | ||
|
||
test_that("pipe_consistency_linter works with %>% argument", { | ||
skip_if_not_r_version("4.1.0") | ||
|
||
linter <- pipe_consistency_linter(pipe = "%>%") | ||
expected_message <- rex("Use the %>% pipe operator instead of the |> pipe operator.") | ||
|
||
expect_lint( | ||
"1:3 |> mean() |> as.character()", | ||
list( | ||
list(message = expected_message, line_number = 1L, column_number = 5L), | ||
list(message = expected_message, line_number = 1L, column_number = 15L) | ||
), | ||
linter | ||
) | ||
|
||
expect_lint( | ||
"1:3 %>% mean() |> as.character()", | ||
list(message = expected_message, line_number = 1L, column_number = 16L), | ||
linter | ||
) | ||
|
||
expect_lint( | ||
"1:3 %>% mean() %>% as.character()", | ||
NULL, | ||
linter | ||
) | ||
|
||
expect_lint( | ||
trim_some(" | ||
1:3 %>% | ||
mean() |> | ||
as.character() | ||
"), | ||
list(message = expected_message, line_number = 2L, column_number = 10L), | ||
linter | ||
) | ||
}) | ||
|
||
test_that("pipe_consistency_linter works with other magrittr pipes", { | ||
skip_if_not_r_version("4.1.0") | ||
linter <- pipe_consistency_linter() | ||
expected_message <- rex("Found 1 instances of %>% and 1 instances of |>. Stick to one pipe operator.") | ||
|
||
expect_lint("1:3 %>% mean() %T% print()", NULL, linter) | ||
expect_lint( | ||
"1:3 |> mean() %T>% print()", | ||
list( | ||
list(message = expected_message, line_number = 1L, column_number = 5L), | ||
list(message = expected_message, line_number = 1L, column_number = 15L) | ||
), | ||
linter | ||
) | ||
}) |
Uh oh!
There was an error while loading. Please reload this page.