11make_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