Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
00aaf53
lint(text=) finds local settings again
MichaelChirico Jun 21, 2025
a257f09
Add a comment
MichaelChirico Jun 21, 2025
b50e4e2
Annotate issue # too
MichaelChirico Jun 21, 2025
159234d
explicitly disable cyclocomp_linter under "hard mode"
MichaelChirico Jun 21, 2025
af4d66b
also need explicit parse_settings=FALSE in cyclocomp test
MichaelChirico Jun 21, 2025
3043aa1
More explicit parse_settings=FALSE
MichaelChirico Jun 22, 2025
07d31f3
must parse .Rproj settings
MichaelChirico Jun 22, 2025
990e3a3
Merge branch 'main' into lint-text-settings
MichaelChirico Jun 30, 2025
1ba8f42
Merge branch 'main' into lint-text-settings
MichaelChirico Jul 14, 2025
83125b3
revert config edit
MichaelChirico Jul 29, 2025
7deac2b
revert
MichaelChirico Jul 29, 2025
05f8fbc
revert
MichaelChirico Jul 29, 2025
f0f4a70
Revert "revert"
MichaelChirico Jul 29, 2025
e760adb
Merge branch 'main' into lint-text-settings
MichaelChirico Jul 29, 2025
9e5894e
Merge branch 'main' into lint-text-settings
MichaelChirico Jul 29, 2025
9fe5af5
amend .lintr to avoid cyclocomp here only
MichaelChirico Jul 29, 2025
90b9d7b
mention breaking change
MichaelChirico Jul 29, 2025
4511bdb
Merge remote-tracking branch 'origin/lint-text-settings' into lint-te…
MichaelChirico Jul 29, 2025
846079a
Merge branch 'main' into lint-text-settings
MichaelChirico Jul 29, 2025
a0401cc
full revert
MichaelChirico Jul 29, 2025
f3337be
only edit NEWS
MichaelChirico Jul 29, 2025
0f028e6
restore once more :)
MichaelChirico Jul 29, 2025
90f9687
delint
MichaelChirico Jul 29, 2025
aa8e5fd
dont parse when missing parse_settings
MichaelChirico Jul 29, 2025
492da73
Merge remote-tracking branch 'origin/lint-text-settings' into lint-te…
MichaelChirico Jul 29, 2025
fa8e9eb
another test
MichaelChirico Jul 29, 2025
891604a
new helper for cyclocomp
MichaelChirico Jul 29, 2025
a4e29a1
missing _
MichaelChirico Jul 29, 2025
42d628c
more typos
MichaelChirico Jul 29, 2025
61bf105
need to pass filename=
MichaelChirico Jul 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
+ Linters `closed_curly_linter()`, `open_curly_linter()`, `paren_brace_linter()`, and `semicolon_terminator_linter()`.
* Argument `interpret_glue` to `object_usage_linter()` is deprecated in favor of the more general `interpret_extensions`, in which `"glue"` is present by default (#1472, @MichaelChirico). See the description below.
* The default for `pipe_consistency_linter()` is changed from `"auto"` (require one pipe style, either magrittr or native) to `"|>"` (R native pipe required) to coincide with the same change in the Tidyverse Style Guide (#2707, @MichaelChirico).
* `lint()` no longer picks up settings automatically in _ad hoc_ invocations like `lint("text\n")` or `lint(text = "str")`. You should set `parse_settings=TRUE` to force settings to be read.

## Bug fixes

* `Lint()`, and thus all linters, ensures that the returned object's `message` attribute is consistently a simple character string (and not, for example, an object of class `"glue"`; #2740, @MichaelChirico).
* Files with encoding inferred from settings read more robustly under `lint(parse_settings = TRUE)` (#2803, @MichaelChirico).
* Files with encoding inferred from settings read more robustly under `lint(parse_settings = TRUE)` (#2803, @MichaelChirico). Thanks also to @bastistician for detecting a regression caused by the initial change for users of Emacs (#2847).
* `assignment_linter()` no longer errors if `"%<>%"` is an allowed operator (#2850, @AshesITR).
* `condition_call_linter()` no longer covers cases where the object type in the ellipsis cannot be determined with certainty (#2888, #2890, @Bisaloo). In particular, this fixes the known false positive of custom conditions created via `errorCondition()` or `warningCondition()` not being compatible with the `call.` argument in `stop()` or `warning()`.

Expand Down
50 changes: 29 additions & 21 deletions R/lint.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ lint <- function(filename, linters = NULL, ..., cache = FALSE, parse_settings =

needs_tempfile <- missing(filename) || re_matches(filename, rex(newline))
inline_data <- !is.null(text) || needs_tempfile
parse_settings <- !inline_data && isTRUE(parse_settings)
parse_settings <- (!inline_data || !missing(parse_settings)) && isTRUE(parse_settings)

if (parse_settings) {
read_settings(filename)
Expand Down Expand Up @@ -77,26 +77,7 @@ lint <- function(filename, linters = NULL, ..., cache = FALSE, parse_settings =
return(exclude(lints, lines = lines, linter_names = names(linters), ...))
}

file_linter_names <- names(linters)[vapply(linters, is_linter_level, logical(1L), "file")]
expression_linter_names <- names(linters)[vapply(linters, is_linter_level, logical(1L), "expression")]

lints <- list()
if (!is_tainted(source_expressions$lines)) {
for (expr in source_expressions$expressions) {
for (linter in necessary_linters(expr, expression_linter_names, file_linter_names)) {
# use withCallingHandlers for friendlier failures on unexpected linter errors
lints[[length(lints) + 1L]] <- withCallingHandlers(
get_lints(expr, linter, linters[[linter]], lint_cache, source_expressions$lines),
error = function(cond) {
cli_abort(
"Linter {.fn linter} failed in {.file {filename}}:",
parent = cond
)
}
)
}
}
}
lints <- lint_impl_(linters, lint_cache, filename, source_expressions)

lints <- maybe_append_condition_lints(lints, source_expressions, lint_cache, filename)
lints <- reorder_lints(flatten_lints(lints))
Expand All @@ -111,6 +92,33 @@ lint <- function(filename, linters = NULL, ..., cache = FALSE, parse_settings =
zap_temp_filename(res, needs_tempfile)
}

lint_impl_ <- function(linters, lint_cache, filename, source_expressions) {
if (is_tainted(source_expressions$lines)) {
return(list())
}

file_linter_names <- names(linters)[vapply(linters, is_linter_level, logical(1L), "file")]
expression_linter_names <- names(linters)[vapply(linters, is_linter_level, logical(1L), "expression")]

lints <- list()
for (expr in source_expressions$expressions) {
for (linter in necessary_linters(expr, expression_linter_names, file_linter_names)) {
# use withCallingHandlers for friendlier failures on unexpected linter errors
lints[[length(lints) + 1L]] <- withCallingHandlers(
get_lints(expr, linter, linters[[linter]], lint_cache, source_expressions$lines),
error = function(cond) {
cli_abort(
"Linter {.fn linter} failed in {.file {filename}}:",
parent = cond
)
}
)
}
}

lints
}

#' @param path For the base directory of the project (for `lint_dir()`) or
#' package (for `lint_package()`).
#' @param relative_path if `TRUE`, file paths are printed using their path relative to the base directory.
Expand Down
2 changes: 2 additions & 0 deletions R/settings.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
read_settings <- function(filename, call = parent.frame()) {
reset_settings()

# doing lint(text=) should read settings from the current directory, required e.g. for Emacs, #2847
if (missing(filename)) filename <- "./any_local_file"
config_file <- find_config(filename)
default_encoding <- find_default_encoding(filename)
if (!is.null(default_encoding)) {
Expand Down
16 changes: 16 additions & 0 deletions tests/testthat/test-lint.R
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,19 @@ test_that("gitlab_output() writes expected report", {
))
)
})

test_that("explicit parse_settings=TRUE works for inline data", {
withr::local_dir(tempdir())
.lintr <- withr::local_tempfile(lines = "linters: list(assignment_linter())")
withr::local_options(list(lintr.linter_file = .lintr))

lint_str <- "a=1\n" # assignment lints, but not infix_spaces
foo <- withr::local_tempfile(lines = lint_str)

expect_length(lint(foo, parse_settings = TRUE), 1L)
expect_length(lint(text = lint_str, parse_settings = TRUE), 1L)
expect_length(lint(lint_str, parse_settings = TRUE), 1L)

# parse_settings=TRUE default not picked up
expect_length(lint(text = lint_str), 2L)
})
Loading