diff --git a/NEWS.md b/NEWS.md index 7bd7fb234..6959d3a87 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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()`. diff --git a/R/lint.R b/R/lint.R index 362336407..e079e11f1 100644 --- a/R/lint.R +++ b/R/lint.R @@ -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) @@ -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)) @@ -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. diff --git a/R/settings.R b/R/settings.R index 48ffcf602..c106ffbd4 100644 --- a/R/settings.R +++ b/R/settings.R @@ -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)) { diff --git a/tests/testthat/test-lint.R b/tests/testthat/test-lint.R index 983c5e0c4..e47fbe339 100644 --- a/tests/testthat/test-lint.R +++ b/tests/testthat/test-lint.R @@ -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) +})