Skip to content

Commit 6713862

Browse files
support for # nolint next (#2090)
* support for # nolint next * review --------- Co-authored-by: AshesITR <alexander.rosenstock@web.de>
1 parent 8613153 commit 6713862

File tree

6 files changed

+98
-22
lines changed

6 files changed

+98
-22
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
## New and improved features
99

10+
* New exclusion sentinel `# nolint next` to signify the next line should skip linting (#1791, @MichaelChirico). The usual rules apply for excluding specific linters, e.g. `# nolint next: assignment_linter.`. The exact string used to match a subsequent-line exclusion is controlled by the `exclude_next` config entry or R option `"lintr.exclude_next"`.
1011
* Linters with logic around the magrittr pipe `%>%` consistently apply it to the other pipes `%!>%`, `%T>%`, `%<>%` (and possibly `%$%`) where appropriate (#2008, @MichaelChirico).
1112
+ `brace_linter()`
1213
+ `pipe_call_linter()`

R/exclude.R

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,20 @@ line_info <- function(line_numbers, type = c("start", "end")) {
8282
#' read a source file and parse all the excluded lines from it
8383
#'
8484
#' @param file R source file
85-
#' @param exclude regular expression used to mark lines to exclude
86-
#' @param exclude_start regular expression used to mark the start of an excluded range
87-
#' @param exclude_end regular expression used to mark the end of an excluded range
88-
#' @param exclude_linter regular expression used to capture a list of to-be-excluded linters immediately following a
85+
#' @param exclude Regular expression used to mark lines to exclude.
86+
#' @param exclude_next Regular expression used to mark lines immediately preceding excluded lines.
87+
#' @param exclude_start Regular expression used to mark the start of an excluded range.
88+
#' @param exclude_end Regular expression used to mark the end of an excluded range.
89+
#' @param exclude_linter Regular expression used to capture a list of to-be-excluded linters immediately following a
8990
#' `exclude` or `exclude_start` marker.
90-
#' @param exclude_linter_sep regular expression used to split a linter list into individual linter names for exclusion.
91-
#' @param lines a character vector of the content lines of `file`
92-
#' @param linter_names Names of active linters
91+
#' @param exclude_linter_sep Regular expression used to split a linter list into individual linter names for exclusion.
92+
#' @param lines A character vector of the content lines of `file`.
93+
#' @param linter_names Names of active linters.
9394
#'
9495
#' @return A possibly named list of excluded lines, possibly for specific linters.
95-
parse_exclusions <- function(file, exclude = settings$exclude,
96+
parse_exclusions <- function(file,
97+
exclude = settings$exclude,
98+
exclude_next = settings$exclude_next,
9699
exclude_start = settings$exclude_start,
97100
exclude_end = settings$exclude_end,
98101
exclude_linter = settings$exclude_linter,
@@ -131,22 +134,34 @@ parse_exclusions <- function(file, exclude = settings$exclude,
131134
}
132135
}
133136

137+
next_locations <- re_matches(lines, exclude_next, locations = TRUE)[, "end"] + 1L
138+
nexts <- which(!is.na(next_locations))
139+
134140
nolint_locations <- re_matches(lines, exclude, locations = TRUE)[, "end"] + 1L
135141
nolints <- which(!is.na(nolint_locations))
136-
# Disregard nolint tags if they also match nolint start / end
137-
nolints <- setdiff(nolints, c(starts, ends))
138142

139-
for (i in seq_along(nolints)) {
140-
linters_string <- substring(lines[nolints[i]], nolint_locations[nolints[i]])
141-
linters_string <- re_matches(linters_string, exclude_linter)[, 1L]
142-
exclusions <- add_exclusions(exclusions, nolints[i], linters_string, exclude_linter_sep, linter_names)
143+
# Disregard nolint tags if they also match nolint next / start / end
144+
nolints <- setdiff(nolints, c(nexts, starts, ends))
145+
146+
for (nolint in nolints) {
147+
linters_string <- get_linters_string(lines[nolint], nolint_locations[nolint], exclude_linter)
148+
exclusions <- add_exclusions(exclusions, nolint, linters_string, exclude_linter_sep, linter_names)
149+
}
150+
for (nextt in nexts) {
151+
linters_string <- get_linters_string(lines[nextt], next_locations[nextt], exclude_linter)
152+
exclusions <- add_exclusions(exclusions, nextt + 1L, linters_string, exclude_linter_sep, linter_names)
143153
}
144154

145155
exclusions[] <- lapply(exclusions, function(lines) sort(unique(lines)))
146156

147157
exclusions
148158
}
149159

160+
get_linters_string <- function(line, loc, exclude_linter) {
161+
linters_string <- substring(line, loc)
162+
re_matches(linters_string, exclude_linter)[, 1L]
163+
}
164+
150165
add_excluded_lines <- function(exclusions, excluded_lines, excluded_linters) {
151166
for (linter in excluded_linters) {
152167
if (linter %in% names2(exclusions)) {

R/zzz.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ settings <- NULL
299299
linters = default_linters,
300300
encoding = "UTF-8",
301301
exclude = rex("#", any_spaces, "nolint"),
302+
exclude_next = rex("#", any_spaces, "nolint next"),
302303
exclude_start = rex("#", any_spaces, "nolint start"),
303304
exclude_end = rex("#", any_spaces, "nolint end"),
304305
exclude_linter = rex(

man/default_settings.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/parse_exclusions.Rd

Lines changed: 10 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-exclusions.R

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,59 @@ test_that("#1442: is_excluded_files works if no global exclusions are specified"
152152
)
153153
expect_length(lint_dir(tmp), 3L)
154154
})
155+
156+
test_that("next-line exclusion works", {
157+
withr::local_options(
158+
lintr.exclude = "# NL",
159+
lintr.exclude_next = "# NLN",
160+
lintr.exlcude_linter = default_settings$exclude_linter
161+
)
162+
163+
linter <- assignment_linter()
164+
165+
# blanket exclusion works
166+
expect_lint(
167+
trim_some("
168+
# NLN
169+
x = 1
170+
"),
171+
NULL,
172+
linter
173+
)
174+
175+
# specific exclusion works
176+
expect_lint(
177+
trim_some("
178+
# NLN: assignment_linter.
179+
x = 1
180+
"),
181+
NULL,
182+
linter
183+
)
184+
expect_lint(
185+
trim_some("
186+
# NLN: assignment.
187+
x = 1
188+
"),
189+
NULL,
190+
linter
191+
)
192+
expect_lint(
193+
trim_some("
194+
# NLN: line_length_linter.
195+
x = 1
196+
"),
197+
rex::rex("Use <-, not =, for assignment."),
198+
list(linter, line_length_linter())
199+
)
200+
201+
# interaction with plain nolint
202+
expect_lint(
203+
trim_some("
204+
x = 1 # NLN: assignment_linter.
205+
x = 2
206+
"),
207+
list(rex::rex("Use <-, not =, for assignment."), line_number = 1L),
208+
linter
209+
)
210+
})

0 commit comments

Comments
 (0)