Skip to content

Commit 209e32c

Browse files
Use new xml_nodes_to_lint() in existing codebase (#964)
1 parent 585e7cd commit 209e32c

8 files changed

+128
-116
lines changed

R/T_and_F_symbol_linter.R

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,23 @@ T_and_F_symbol_linter <- function() { # nolint: object_name_linter.
3636
bad_exprs <- xml2::xml_find_all(source_file$xml_parsed_content, xpath)
3737
bad_assigns <- xml2::xml_find_all(source_file$xml_parsed_content, xpath_assignment)
3838

39+
replacement_map <- c(T = "TRUE", F = "FALSE")
3940
make_lint <- function(expr, fmt) {
40-
symbol <- xml2::xml_text(expr)
41-
replacement <- switch(symbol, "T" = "TRUE", "F" = "FALSE")
42-
message <- sprintf(fmt, replacement, symbol)
4341
xml_nodes_to_lint(
4442
xml = expr,
4543
source_file = source_file,
46-
lint_message = message,
44+
lint_message = function(expr) {
45+
symbol <- xml2::xml_text(expr)
46+
sprintf(fmt, replacement_map[[symbol]], symbol)
47+
},
4748
type = "style",
4849
offset = 1L
4950
)
5051
}
5152

5253
c(
53-
lapply(
54-
bad_exprs, make_lint,
55-
fmt = "Use %s instead of the symbol %s."
56-
),
57-
lapply(
58-
bad_assigns, make_lint,
59-
fmt = "Don't use %2$s as a variable name, as it can break code relying on %2$s being %1$s."
60-
)
54+
lapply(bad_exprs, make_lint, fmt = "Use %s instead of the symbol %s."),
55+
lapply(bad_assigns, make_lint, fmt = "Don't use %2$s as a variable name, as it can break code relying on %2$s being %1$s.")
6156
)
6257
})
6358
}

R/assignment_linter.R

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,22 @@ assignment_linter <- function(allow_cascading_assign = TRUE, allow_right_assign
2727
))
2828

2929
bad_expr <- xml2::xml_find_all(xml, xpath)
30-
lapply(bad_expr, gen_assignment_lint, source_file)
31-
})
32-
}
33-
34-
gen_assignment_lint <- function(expr, source_file) {
35-
operator <- xml2::xml_text(expr)
36-
if (operator %in% c("<<-", "->>")) {
37-
message <- sprintf(
38-
"%s can have hard-to-predict behavior; prefer assigning to a specific environment instead (with assign() or <-).",
39-
operator
30+
lapply(
31+
bad_expr,
32+
xml_nodes_to_lint,
33+
source_file,
34+
function(expr) {
35+
operator <- xml2::xml_text(expr)
36+
if (operator %in% c("<<-", "->>")) {
37+
sprintf(
38+
"%s can have hard-to-predict behavior; prefer assigning to a specific environment instead (with assign() or <-).",
39+
operator
40+
)
41+
} else {
42+
sprintf("Use <-, not %s, for assignment.", operator)
43+
}
44+
},
45+
type = "style"
4046
)
41-
} else {
42-
message <- sprintf("Use <-, not %s, for assignment.", operator)
43-
}
44-
xml_nodes_to_lint(expr, source_file, message, type = "style")
47+
})
4548
}

R/expect_length_linter.R

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@ expect_length_linter <- function() {
2525
]")
2626

2727
bad_expr <- xml2::xml_find_all(xml, xpath)
28-
return(lapply(bad_expr, gen_expect_length_lint, source_file))
28+
return(lapply(
29+
bad_expr,
30+
xml_nodes_to_lint,
31+
source_file,
32+
function(expr) {
33+
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
34+
sprintf("expect_length(x, n) is better than %s(length(x), n)", matched_function)
35+
},
36+
type = "warning"
37+
))
2938
})
3039
}
31-
32-
gen_expect_length_lint <- function(expr, source_file) {
33-
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
34-
lint_msg <- sprintf("expect_length(x, n) is better than %s(length(x), n)", matched_function)
35-
xml_nodes_to_lint(expr, source_file, lint_msg, type = "warning")
36-
}

R/expect_null_linter.R

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,19 @@ expect_null_linter <- function() {
3434

3535
bad_expr <- xml2::xml_find_all(xml, xpath)
3636

37-
lapply(bad_expr, gen_expect_null_lint, source_file)
37+
lapply(
38+
bad_expr,
39+
xml_nodes_to_lint,
40+
source_file,
41+
function(expr) {
42+
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
43+
if (matched_function %in% c("expect_equal", "expect_identical")) {
44+
sprintf("expect_null(x) is better than %s(x, NULL)", matched_function)
45+
} else {
46+
"expect_null(x) is better than expect_true(is.null(x))"
47+
}
48+
},
49+
type = "warning"
50+
)
3851
})
3952
}
40-
41-
gen_expect_null_lint <- function(expr, source_file) {
42-
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
43-
if (matched_function %in% c("expect_equal", "expect_identical")) {
44-
lint_msg <- sprintf("expect_null(x) is better than %s(x, NULL)", matched_function)
45-
} else {
46-
lint_msg <- "expect_null(x) is better than expect_true(is.null(x))"
47-
}
48-
xml_nodes_to_lint(expr, source_file, lint_msg, type = "warning")
49-
}

R/expect_s3_class_linter.R

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,21 @@ expect_s3_class_linter <- function() {
3333
]")
3434

3535
bad_expr <- xml2::xml_find_all(xml, xpath)
36-
return(lapply(bad_expr, gen_expect_s3_class_lint, source_file))
36+
return(lapply(
37+
bad_expr,
38+
xml_nodes_to_lint,
39+
source_file,
40+
function(expr) {
41+
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
42+
if (matched_function %in% c("expect_equal", "expect_identical")) {
43+
lint_msg <- sprintf("expect_s3_class(x, k) is better than %s(class(x), k).", matched_function)
44+
} else {
45+
lint_msg <- "expect_s3_class(x, k) is better than expect_true(is.<k>(x)) or expect_true(inherits(x, k))."
46+
}
47+
paste(lint_msg, "Note also expect_s4_class() available for testing S4 objects.")
48+
},
49+
type = "warning"
50+
))
3751
})
3852
}
3953

@@ -55,17 +69,6 @@ is_s3_class_calls <- paste0("is.", c(
5569
"mts", "stepfun", "ts", "tskernel"
5670
))
5771

58-
gen_expect_s3_class_lint <- function(expr, source_file) {
59-
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
60-
if (matched_function %in% c("expect_equal", "expect_identical")) {
61-
lint_msg <- sprintf("expect_s3_class(x, k) is better than %s(class(x), k).", matched_function)
62-
} else {
63-
lint_msg <- "expect_s3_class(x, k) is better than expect_true(is.<k>(x)) or expect_true(inherits(x, k))."
64-
}
65-
lint_msg <- paste(lint_msg, "Note also expect_s4_class() available for testing S4 objects.")
66-
xml_nodes_to_lint(expr, source_file, lint_msg, type = "warning")
67-
}
68-
6972
#' Require usage of expect_s4_class(x, k) over expect_true(is(x, k))
7073
#'
7174
#' [testthat::expect_s4_class()] exists specifically for testing the class

R/expect_true_false_linter.R

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,21 @@ expect_true_false_linter <- function() {
2222
]]"
2323

2424
bad_expr <- xml2::xml_find_all(xml, xpath)
25-
return(lapply(bad_expr, gen_expect_true_false_lint, source_file))
25+
return(lapply(
26+
bad_expr,
27+
xml_nodes_to_lint,
28+
source_file,
29+
function(expr) {
30+
# NB: use expr/$node, not expr[$node], to exclude other things (especially ns:: parts of the call)
31+
call_name <- xml2::xml_text(xml2::xml_find_first(expr, "expr/SYMBOL_FUNCTION_CALL[starts-with(text(), 'expect_')]"))
32+
truth_value <- xml2::xml_text(xml2::xml_find_first(expr, "expr/NUM_CONST[text() = 'TRUE' or text() = 'FALSE']"))
33+
if (truth_value == "TRUE") {
34+
sprintf("expect_true(x) is better than %s(x, TRUE)", call_name)
35+
} else {
36+
sprintf("expect_false(x) is better than %s(x, FALSE)", call_name)
37+
}
38+
},
39+
type = "warning"
40+
))
2641
})
2742
}
28-
29-
gen_expect_true_false_lint <- function(expr, source_file) {
30-
# NB: use expr/$node, not expr[$node], to exclude other things (especially ns:: parts of the call)
31-
call_name <- xml2::xml_text(xml2::xml_find_first(expr, "expr/SYMBOL_FUNCTION_CALL[starts-with(text(), 'expect_')]"))
32-
truth_value <- xml2::xml_text(xml2::xml_find_first(expr, "expr/NUM_CONST[text() = 'TRUE' or text() = 'FALSE']"))
33-
if (truth_value == "TRUE") {
34-
lint_msg <- sprintf("expect_true(x) is better than %s(x, TRUE)", call_name)
35-
} else {
36-
lint_msg <- sprintf("expect_false(x) is better than %s(x, FALSE)", call_name)
37-
}
38-
xml_nodes_to_lint(expr, source_file, lint_msg, type = "warning")
39-
}

R/expect_type_linter.R

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,23 @@ expect_type_linter <- function() {
3131
]")
3232

3333
bad_expr <- xml2::xml_find_all(xml, xpath)
34-
return(lapply(bad_expr, gen_expect_type_lint, source_file))
34+
return(lapply(
35+
bad_expr,
36+
xml_nodes_to_lint,
37+
source_file,
38+
function(expr) {
39+
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
40+
if (matched_function %in% c("expect_equal", "expect_identical")) {
41+
sprintf("expect_type(x, t) is better than %s(typeof(x), t)", matched_function)
42+
} else {
43+
"expect_type(x, t) is better than expect_true(is.<t>(x))"
44+
}
45+
},
46+
type = "warning"
47+
))
3548
})
3649
}
3750

38-
gen_expect_type_lint <- function(expr, source_file) {
39-
matched_function <- xml2::xml_text(xml2::xml_find_first(expr, "SYMBOL_FUNCTION_CALL"))
40-
if (matched_function %in% c("expect_equal", "expect_identical")) {
41-
lint_msg <- sprintf("expect_type(x, t) is better than %s(typeof(x), t)", matched_function)
42-
} else {
43-
lint_msg <- "expect_type(x, t) is better than expect_true(is.<t>(x))"
44-
}
45-
xml_nodes_to_lint(expr, source_file, lint_msg, type = "warning")
46-
}
47-
48-
4951
# NB: the full list of values that can arise from `typeof(x)` is available
5052
# in ?typeof (or, slightly more robustly, in the R source: src/main/util.c.
5153
# Not all of them are available in is.<type> form, e.g. 'any' or

R/package_hooks_linter.R

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ package_hooks_linter <- function() {
5252
bad_msg_call_lints <- function(hook) {
5353
xpath <- sprintf(bad_msg_call_xpath_fmt, hook, xp_text_in_table(bad_calls[[hook]]))
5454
bad_expr <- xml2::xml_find_all(xml, xpath)
55-
lapply(bad_expr, make_bad_call_lint, source_file, hook)
55+
lapply(bad_expr, xml_nodes_to_lint, source_file, make_bad_call_lint_msg(hook), type = "warning")
5656
}
5757

5858
onload_bad_msg_call_lints <- bad_msg_call_lints(".onLoad")
@@ -77,13 +77,13 @@ package_hooks_linter <- function() {
7777

7878
load_arg_name_lints <- lapply(
7979
load_arg_name_expr,
80-
function(expr) {
81-
message <- sprintf(
82-
"%s() should take two arguments, with the first starting with 'lib' and the second starting with 'pkg'.",
83-
get_hook(expr)
84-
)
85-
xml_nodes_to_lint(expr, source_file, message, type = "warning")
86-
}
80+
xml_nodes_to_lint,
81+
source_file,
82+
function(expr) sprintf(
83+
"%s() should take two arguments, with the first starting with 'lib' and the second starting with 'pkg'.",
84+
get_hook(expr)
85+
),
86+
type = "warning"
8787
)
8888

8989
# (3) .onLoad() and .onAttach() shouldn't call require(), library(), or installed.packages()
@@ -100,16 +100,18 @@ package_hooks_linter <- function() {
100100

101101
library_require_lints <- lapply(
102102
library_require_expr,
103+
xml_nodes_to_lint,
104+
source_file,
103105
function(expr) {
104106
bad_call <- xml2::xml_text(expr)
105107
hook <- get_hook(expr)
106108
if (bad_call == "installed.packages") {
107-
message <- sprintf("Don't slow down package load by running installed.packages() in %s().", hook)
109+
sprintf("Don't slow down package load by running installed.packages() in %s().", hook)
108110
} else {
109-
message <- sprintf("Don't alter the search() path in %s() by calling %s().", hook, bad_call)
111+
sprintf("Don't alter the search() path in %s() by calling %s().", hook, bad_call)
110112
}
111-
xml_nodes_to_lint(expr, source_file, message, type = "warning")
112-
}
113+
},
114+
type = "warning"
113115
)
114116

115117
# (4) .Last.lib() and .onDetach() shouldn't call library.dynam.unload()
@@ -123,10 +125,10 @@ package_hooks_linter <- function() {
123125

124126
bad_unload_call_lints <- lapply(
125127
bad_unload_call_expr,
126-
function(expr) {
127-
message <- sprintf("Use library.dynam.unload() calls in .onUnload(), not %s().", get_hook(expr))
128-
xml_nodes_to_lint(expr, source_file, message, type = "warning")
129-
}
128+
xml_nodes_to_lint,
129+
source_file,
130+
function(expr) sprintf("Use library.dynam.unload() calls in .onUnload(), not %s().", get_hook(expr)),
131+
type = "warning"
130132
)
131133

132134
# (5) .Last.lib() and .onDetach() should take one arguments with name matching ^lib
@@ -145,13 +147,10 @@ package_hooks_linter <- function() {
145147

146148
unload_arg_name_lints <- lapply(
147149
unload_arg_name_expr,
148-
function(expr) {
149-
message <- sprintf(
150-
"%s() should take one argument starting with 'lib'.",
151-
get_hook(expr)
152-
)
153-
xml_nodes_to_lint(expr, source_file, message, type = "warning")
154-
}
150+
xml_nodes_to_lint,
151+
source_file,
152+
function(expr) sprintf("%s() should take one argument starting with 'lib'.", get_hook(expr)),
153+
type = "warning"
155154
)
156155

157156
return(c(
@@ -165,16 +164,17 @@ package_hooks_linter <- function() {
165164
})
166165
}
167166

168-
make_bad_call_lint <- function(expr, source_file, hook) {
169-
call_name <- xml2::xml_text(expr)
170-
message <- switch(
171-
call_name,
172-
cat = ,
173-
message = ,
174-
print = ,
175-
writeLines = sprintf("Don't use %s() in %s().", call_name, hook),
176-
packageStartupMessage = "Put packageStartupMessage() calls in .onAttach(), not .onLoad().",
177-
library.dynam = "Put library.dynam() calls in .onLoad, not .onAttach()."
178-
)
179-
xml_nodes_to_lint(expr, source_file, message, type = "warning")
167+
make_bad_call_lint_msg <- function(hook) {
168+
function(expr) {
169+
call_name <- xml2::xml_text(expr)
170+
switch(
171+
call_name,
172+
cat = ,
173+
message = ,
174+
print = ,
175+
writeLines = sprintf("Don't use %s() in %s().", call_name, hook),
176+
packageStartupMessage = "Put packageStartupMessage() calls in .onAttach(), not .onLoad().",
177+
library.dynam = "Put library.dynam() calls in .onLoad, not .onAttach()."
178+
)
179+
}
180180
}

0 commit comments

Comments
 (0)