/
unnecessary_placeholder_linter.R
67 lines (64 loc) · 1.85 KB
/
unnecessary_placeholder_linter.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#' Block usage of pipeline placeholders if unnecessary
#'
#' The argument placeholder `.` in magrittr pipelines is unnecessary if
#' passed as the first positional argument; using it can cause confusion
#' and impacts readability.
#'
#' This is true for forward (`%>%`), assignment (`%<>%`), and tee (`%T>%`) operators.
#'
#' @examples
#' # will produce lints
#' lint(
#' text = "x %>% sum(., na.rm = TRUE)",
#' linters = unnecessary_placeholder_linter()
#' )
#'
#' # okay
#' lint(
#' text = "x %>% sum(na.rm = TRUE)",
#' linters = unnecessary_placeholder_linter()
#' )
#'
#' lint(
#' text = "x %>% lm(data = ., y ~ z)",
#' linters = unnecessary_placeholder_linter()
#' )
#'
#' lint(
#' text = "x %>% outer(., .)",
#' linters = unnecessary_placeholder_linter()
#' )
#'
#' @evalRd rd_tags("unnecessary_placeholder_linter")
#' @seealso [linters] for a complete list of linters available in lintr.
#' @export
unnecessary_placeholder_linter <- function() {
# NB: Native placeholder '_' must be used with a named argument, so it's not relevant here.
xpath <- glue("
//SPECIAL[{ xp_text_in_table(magrittr_pipes) }]
/following-sibling::expr[
expr/SYMBOL_FUNCTION_CALL
and not(expr[
position() > 2
and descendant-or-self::expr/SYMBOL[text() = '.']
])
]
/expr[2][
SYMBOL[text() = '.']
and not(preceding-sibling::*[1][self::EQ_SUB])
]
")
Linter(linter_level = "expression", function(source_expression) {
xml <- source_expression$xml_parsed_content
bad_expr <- xml_find_all(xml, xpath)
xml_nodes_to_lints(
bad_expr,
source_expression = source_expression,
lint_message = paste(
"Don't use the placeholder (`.`) when it's not needed,",
"i.e., when it's only used as the first positional argument in a pipeline step."
),
type = "warning"
)
})
}