Skip to content

Commit c3c10bb

Browse files
Use file-level expressions and avoid slow loop for whitespace_linter() for up to 50% speedup (#2024)
* Use file-level expressions for whitespace_linter() for up to 50% speedup * use full-source names everywhere * better tailor make_linter_from_regex to the whitespace case * trailing ws * eliminate 'ignore_strings' option
1 parent 1b459fc commit c3c10bb

File tree

1 file changed

+26
-52
lines changed

1 file changed

+26
-52
lines changed

R/make_linter_from_regex.R

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,53 @@
11
make_linter_from_regex <- function(regex,
22
lint_type,
3-
lint_msg,
4-
ignore_strings = TRUE) {
5-
# If a regex-based linter is found, only flag those lints that occur within
6-
# a relevant section of source code
7-
.in_ignorable_position <- function(source_expression, line_number, match) {
8-
ignore_strings && in_string(source_expression, line_number, match)
9-
}
10-
3+
lint_msg) {
114
function() {
125
Linter(function(source_expression) {
13-
if (!is_lint_level(source_expression, "expression")) {
6+
if (!is_lint_level(source_expression, "file")) {
147
return(list())
158
}
169

1710
all_matches <- re_matches(
18-
source_expression[["lines"]],
11+
source_expression[["file_lines"]],
1912
regex,
2013
locations = TRUE,
21-
global = TRUE
14+
global = FALSE
2215
)
16+
all_matches <- all_matches[!is.na(all_matches$start), ]
17+
all_matches$line_number <- as.integer(rownames(all_matches))
2318

24-
line_numbers <- as.integer(names(source_expression[["lines"]]))
25-
26-
lints <- Map(
27-
function(line_matches, line_number) {
28-
lapply(
29-
split(line_matches, seq_len(nrow(line_matches))),
30-
function(.match) {
31-
if (
32-
is.na(.match[["start"]]) ||
33-
.in_ignorable_position(source_expression, line_number, .match)
34-
) {
35-
return()
36-
}
37-
start <- .match[["start"]]
38-
end <- .match[["end"]]
39-
Lint(
40-
filename = source_expression[["filename"]],
41-
line_number = line_number,
42-
column_number = start,
43-
type = lint_type,
44-
message = lint_msg,
45-
line = source_expression[["lines"]][[as.character(line_number)]],
46-
ranges = list(c(start, end))
47-
)
48-
}
49-
)
50-
},
51-
all_matches,
52-
line_numbers
53-
)
19+
matches_by_row <- split(all_matches, seq_len(nrow(all_matches)))
5420

55-
Filter(function(x) any(lengths(x) > 0L), lints)
21+
lints <- lapply(matches_by_row, function(.match) {
22+
if (is_match_covered(.match, source_expression)) {
23+
return()
24+
}
25+
Lint(
26+
filename = source_expression[["filename"]],
27+
line_number = .match$line_number,
28+
type = lint_type,
29+
message = lint_msg,
30+
line = source_expression[["file_lines"]][[rownames(.match)]],
31+
ranges = list(c(.match$start, .match$end))
32+
)
33+
})
34+
lints[lengths(lints) > 0L]
5635
})
5736
}
5837
}
5938

6039
#' Determine if a regex match is covered by an expression in a source_expression
6140
#'
41+
#' @param match The position where a regex match was observed.
42+
#' match must have entries "start", "end", and "line_number".
6243
#' @param source_expression A source_expression
63-
#' @param line_number,match The position where a regex match was observed.
64-
#' match must have entries "start" and "end".
6544
#' @param token_type Restrict analysis to tokens of this type, for example,
6645
#' with token_type = "STR_CONST" you can check that a regex match occurs
6746
#' within a string
6847
#' @noRd
69-
is_match_covered <- function(source_expression, line_number, match, token_type = NULL) {
70-
pc <- source_expression[["parsed_content"]]
48+
is_match_covered <- function(match, source_expression, token_type = "STR_CONST") {
49+
line_number <- match$line_number
50+
pc <- source_expression[["full_parsed_content"]]
7151
if (!is.null(token_type)) {
7252
pc <- pc[pc[["token"]] == token_type, ]
7353
}
@@ -92,9 +72,3 @@ is_match_covered <- function(source_expression, line_number, match, token_type =
9272

9373
any_single_line_covers() || any_multi_line_covers()
9474
}
95-
96-
in_string <- function(source_expression, line_number, match) {
97-
# do any of the strings in the parsed content contain the matched regex?
98-
99-
is_match_covered(source_expression, line_number, match, "STR_CONST")
100-
}

0 commit comments

Comments
 (0)