Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
* `sprintf_linter()` lints `sprintf()` and `gettextf()` calls when a constant string is passed to `fmt` (#2894, @Bisaloo).
* `use_lintr()` adds the created `.lintr` file to the `.Rbuildignore` if run in a package (#2926, initial work by @MEO265, finalized by @Bisaloo).
* `length_test_linter()` is extended to check incorrect usage of `nrow()`, `ncol()`, `NROW()`, `NCOL()` (#2933, @mcol).
* `implicit_assignment_linter()` gains argument `allow_paren_print` to disable lints for the use of `(` for auto-printing (#2962, @TimTaylor).

### New linters

Expand Down
19 changes: 18 additions & 1 deletion R/implicit_assignment_linter.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#' @param allow_scoped Logical, default `FALSE`. If `TRUE`, "scoped assignments",
#' where the object is assigned in the statement beginning a branch and used only
#' within that branch, are skipped.
#' @param allow_paren_print Logical, default `FALSE`. If `TRUE`, assignments using
#' `(` for auto-printing at the top-level are not linted.
#'
#' @examples
#' # will produce lints
Expand All @@ -22,6 +24,12 @@
#' linters = implicit_assignment_linter()
#' )
#'
#' lint(
#' text = "(x <- 1)",
#' linters = implicit_assignment_linter()
#' )
#'
#'
#' # okay
#' lines <- "x <- 1L\nif (x) TRUE"
#' writeLines(lines)
Expand Down Expand Up @@ -53,6 +61,11 @@
#' linters = implicit_assignment_linter(allow_scoped = TRUE)
#' )
#'
#' lint(
#' text = "(x <- 1)",
#' linters = implicit_assignment_linter(allow_paren_print = TRUE)
#' )
#'
#' @evalRd rd_tags("implicit_assignment_linter")
#' @seealso
#' - [linters] for a complete list of linters available in lintr.
Expand All @@ -61,7 +74,8 @@
#' @export
implicit_assignment_linter <- function(except = c("bquote", "expression", "expr", "quo", "quos", "quote"),
allow_lazy = FALSE,
allow_scoped = FALSE) {
allow_scoped = FALSE,
allow_paren_print = FALSE) {
stopifnot(is.null(except) || is.character(except))

if (length(except) > 0L) {
Expand Down Expand Up @@ -116,6 +130,9 @@ implicit_assignment_linter <- function(except = c("bquote", "expression", "expr"
bad_expr <- xml_find_all(xml, xpath)

print_only <- !is.na(xml_find_first(bad_expr, "parent::expr[parent::exprlist and *[1][self::OP-LEFT-PAREN]]"))
if (allow_paren_print) {
bad_expr <- bad_expr[!print_only]
}

xml_nodes_to_lints(
bad_expr,
Expand Down
17 changes: 16 additions & 1 deletion man/implicit_assignment_linter.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions tests/testthat/test-implicit_assignment_linter.R
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,20 @@ test_that("call-less '(' mentions avoiding implicit printing", {
linter
)
})

test_that("allow_paren_print allows `(` for auto printing", {
lint_message <- rex::rex("Avoid implicit assignments in function calls.")
linter <- implicit_assignment_linter(allow_paren_print = TRUE)
expect_no_lint("(a <- foo())", linter)

# Doesn't effect other cases
lint_message <- rex::rex("Avoid implicit assignments in function calls.")
expect_lint("if (x <- 1L) TRUE", lint_message, linter)
expect_lint("while (x <- 0L) FALSE", lint_message, linter)
expect_lint("for (x in 1:10 -> y) print(x)", lint_message, linter)
expect_lint("mean(x <- 1:4)", lint_message, linter)

# default remains as is
print_msg <- rex::rex("Call print() explicitly instead of relying on implicit printing behavior via '('.")
expect_lint("(a <- foo())", print_msg, implicit_assignment_linter())
})
Loading