Subject: CVS commit: pkgsrc/devel/R-lintr
From: Makoto Fujiwara
Date: 2025-02-16 01:20:00
Message id: 20250216002000.C0F7CFBE0@cvs.NetBSD.org

Log Message:
(devel/R-lintr) Updated 3.1.2 to 3.2.0

# lintr (development version)

## Deprecations & breaking changes

* Various things marked deprecated since {lintr} 3.0.0 have been fully
  deprecated. They will be completely removed in the subsequent
  release. See previous NEWS for advice on how to replace them.
   + `source_file=` argument to `ids_with_token()` and `with_id()`.
   + Passing linters by name or as non-`"linter"`-classed functions.
   + `linter=` argument of `Lint()`.
   + `with_defaults()`.
   + Linters `closed_curly_linter()`, `open_curly_linter()`,
   `paren_brace_linter()`, and `semicolon_terminator_linter()`.
   + Helper `with_defaults()`.

* `all_linters()` has signature `all_linters(..., packages)` rather
  than `all_linters(packages, ...)` (#2332, @MichaelChirico). This
  forces `packages=` to be supplied by name and will break users who
  rely on supplying `packages=` positionally, of which we found none
  searching GitHub.

* Adjusted various lint messages for consistency and readability
  (#1330, @MichaelChirico). In general, we favor lint messages to be
  phrased like "Action, reason" to put the "what" piece of \ 
the message
  front-and-center. This may be a breaking change for code that tests
  the specific phrasing of lints.

* `extraction_operator_linter()` is deprecated. Although switching
  from `$` to `[[` has some robustness benefits for package code, it
  can lead to non-idiomatic code in many contexts (e.g. R6 classes,
  Shiny applications, etc.) (#2409, @IndrajeetPatil). One reason to
  avoid `$` is that it allows partial matching where `[[` does
  not. Use `options(warnPartialMatchDollar = TRUE)` to disable this
  feature and restore some parity to using `$` vs. `[[`.

* `unnecessary_nested_if_linter()` is deprecated and subsumed into the
  new/more general `unnecessary_nesting_linter()`.

* Dropped support for posting GitHub comments from inside GitHub
  comment bot, Travis, Wercker, and Jenkins CI tools (spurred by
  #2148, @MichaelChirico). We rely on GitHub Actions for linting in
  CI, and don't see any active users relying on these alternatives. We
  welcome and encourage community contributions to get support for
  different CI systems going again.

* `cyclocomp_linter()` is no longer part of the default linters
  (#2555, @IndrajeetPatil) because the tidyverse style guide doesn't
  contain any guidelines on meeting certain complexity
  requirements. With this, we also downgrade {cyclocomp} from
  `Imports:` to `Suggests:`. Note that users with `cyclocomp_linter()`
;  in their configs may now need to install {cyclocomp} intentionally,
  in particular in CI/CD pipelines.

* `scalar_in_linter()` is now configurable to allow other `%in%`-like
  operators to be linted. The data.table operator `%chin%` is no
  longer linted by default; use `in_operators = "%chin%"` to continue
  linting it. (@F-Noelle)

* `lint()` and friends now normalize paths to forward slashes on
  Windows (@olivroy, #2613).

* `undesirable_function_linter()`, `undesirable_operator_linter()`,
  and `list_comparison_linter()` were removed from the tag
  `efficiency` (@IndrajeetPatil, #2655). If you use
  `linters_with_tags("efficiency")` to include these linters, you'll
  need to adjust your config to keep linting your code against
  them. We did not find any such users on GitHub.

* Arguments `allow_cascading_assign=`, `allow_right_assign=`, and
  `allow_pipe_assign=` to `assignment_linter()` are all deprecated in
  favor of the new `operator=` argument. Usage of a positional first
  argument like `assignment_linter(TRUE)`, of which we found zero
  cases on GitHub, is totally deprecated to allow `operator=` to be
  positionally first. See below about the new argument.

## Bug fixes

* `expect_identical_linter()` also skips `expect_equal()` comparison
  to _negative_ non-integers like `-1.034` (#2411, @Bisaloo). This is
  a parity fix since _positive_ reals have always been skipped because
  "high-precision" comparisons are typically done to get tests within
  `tolerance`, so `expect_identical()` is not a great substitution.

* `object_name_linter()` no longer errors when user-supplied
  `regexes=` have capture groups (#2188, @MichaelChirico).

* `.lintr` config validation correctly accepts regular expressions
  which only compile under `perl = TRUE` (#2375,
  @MichaelChirico). These have always been valid (since
  `rex::re_matches()`, which powers the lint exclusion logic, also
  uses this setting), but the new up-front validation in v3.1.1
  incorrectly used `perl = FALSE`.

* `.lintr` configs set by option `lintr.linter_file` or environment
  variable `R_LINTR_LINTER_FILE` can point to subdirectories (#2512,
  @MichaelChirico).

* `indentation_linter()` returns lints with `ranges[1L]==1L` when the
  offending line has 0 spaces (#2550, @MichaelChirico).

* `literal_coercion_linter()` doesn't surface a warning about `NA`s
  during coercion for code like `as.integer("a")` (#2566,
  @MichaelChirico).

## Changes to default linters

* New default linter `return_linter()` for the style guide rule that
  terminal returns should be left implicit (#1100, #2343, #2354, and
  #2356, @MEO265 and @MichaelChirico).

## New and improved features

* New function node caching for big efficiency gains to most linters
  (e.g. overall `lint_package()` improvement of 14-27% and core
  linting improvement up to 30%; #2357, @AshesITR). Most linters are
  written around function usage, and XPath performance searching for
  many functions is poor. The new `xml_find_function_calls()` entry in
  the `get_source_expressions()` output caches all function call nodes
  instead. See the vignette on creating linters for more details on
  how to use it.

* `Linter()` has a new argument `linter_level=` (default `NA`). This
  is used by `lint()` to more efficiently check for expression levels
  than the idiom `if (!is_lint_level(...)) { return(list()) }` (#2351,
  @AshesITR).

* New `return_linter()` also has arguments for fine-tuning which
  functions get linted:

  + `return_style=` (`"implicit"` by default) which checks that all
  functions confirm to the specified return style of `"implicit"` or
  `"explicit"` (#2271 and part of #884, @MichaelChirico, @AshesITR and
  @MEO265).

  + `allow_implicit_else=` (default `TRUE`) which, when `FALSE`,
  checks that all terminal `if` statements are paired with a
  corresponding `else` statement (part of #884, @MichaelChirico).

  + `return_functions=` to customize which functions are equivalent to
  `return()` as "exit" clauses, e.g. `rlang::abort()` can be
  considered in addition to the default functions like `stop()` and
  `q()` from base (#2271 and part of #884, @MichaelChirico and
  @MEO265).

  + `except=` to customize which functions are ignored entirely (i.e.,
  whether they have a return of the specified style is not checked;
  #2271 and part of #884, @MichaelChirico and @MEO265). Namespace
  hooks like `.onAttach()` and `.onLoad()` are always ignored.

  + `except_regex=`, the same purpose as `except=`, but filters
  functions by pattern. This is motivated by {RUnit}, where test
  suites are based on unit test functions matched by pattern,
  e.g. `^Test`, and where explicit return may be awkward (#2335,
  @MichaelChirico).

* `assignment_linter()` can be fully customized with the new
  `operator=` argument to specify an exact vector of assignment
  operators to allow (#2441, @MichaelChirico and @J-Moravec). The
  default is `<-` and `<<-`; authors wishing to use `=` (only) for
  assignment in their codebase can use `operator = "="`. This
  supersedes several old arguments: to accomplish
  `allow_cascading_assign=TRUE`, add `"<<-"` (and/or \ 
`"->>"`) to
  `operator=`; for `allow_right_assign=TRUE`, add `"->"` (and/or
  `"->>"`) to `operator=`; for `allow_pipe_assign=TRUE`, add \ 
`"%<>%"`
  to `operator=`. Use `operator = "any"` to denote "ignore all
  assignment operators"; in this case, only the value of
  `allow_trailing=` matters. Implicit assignments with `<-` are always
  ignored by `assignment_linter()`; use `implicit_assignment_linter()`
  to handle linting these.

* More helpful errors for invalid configs (#2253, @MichaelChirico).

* `library_call_linter()` is extended

  + to encourage all packages to be attached with `library(symbol)`,
  not `library("symbol", character.only = TRUE)` or "vectorized"
  approaches looping over package names (part of #884,
  @MichaelChirico).

  + to discourage many consecutive calls to `suppressMessages()` or
  `suppressPackageStartupMessages()` (part of #884, @MichaelChirico).

* `unnecessary_lambda_linter()` is extended to encourage vectorized
  comparisons where possible, e.g. `sapply(x, sum) > 0` instead of
  `sapply(x, function(x) sum(x) > 0)` (part of #884,
  @MichaelChirico). Toggle this behavior with argument
  `allow_comparison=`.

* `backport_linter()` is slightly faster by moving expensive
  computations outside the linting function (#2339, #2348, @AshesITR
  and @MichaelChirico).

* `string_boundary_linter()` recognizes regular expression calls like
  `grepl("^abc$", x)` that can be replaced by using `==` instead
  (#1613, @MichaelChirico).

* `unreachable_code_linter()` has an argument `allow_comment_regex=`
  for customizing which "terminal" comments to exclude (#2327,
  @MichaelChirico). Exclusion comments from {lintr} and {covr}
  (e.g. `# nocov end`) are always excluded.

* `format()` and `print()` methods for `lint` and `lints` classes get
  a new option `width=` to control the printing width of lint messages
  (#1884, @MichaelChirico). The default is controlled by a new option
  `lintr.format_width`; if unset, no wrapping occurs (matching earlier
  behavior).

* `implicit_assignment_linter()` gets a custom message for the case of
  using `(` to induce printing like `(x <- foo())`; use an explicit
  call to `print()` for clarity (#2257, @MichaelChirico).

* `todo_comment_linter()` has a new argument `except_regex=` for
  setting _valid_ TODO comments, e.g. for forcing TODO comments to be
  linked to GitHub issues like `TODO(#154)` (#2047, @MichaelChirico).

* `vector_logic_linter()` is extended to recognize incorrect usage of
  scalar operators `&&` and `||` inside subsetting expressions like
  `dplyr::filter(x, A && B)` (#2166, @MichaelChirico).

* `any_is_na_linter()` is extended to catch the unusual usage `NA %in%
  x` (#2113, @MichaelChirico).

* `make_linter_from_xpath()` errors up front when `lint_message=` is
  missing (instead of delaying this error until the linter is used,
  #2541, @MichaelChirico).

* `paste_linter()` is extended to recommend using `paste()` instead of
  `paste0()` for simply aggregating a character vector with
  `collapse=`, i.e., when `sep=` is irrelevant (#1108,
  @MichaelChirico).

* `expect_no_lint()` was added as new function to cover the typical
  use case of expecting no lint message, akin to the recent {testthat}
  functions like `expect_no_warning()` (#2580, @F-Noelle).

* `lint()` and friends emit a message if no lints are found (#2643,
  @IndrajeetPatil).

* `commented_code_linter()` can detect commented code that ends with a
  pipe (#2671, @jcken95)

### New linters

* `condition_call_linter()` for ensuring consistent use of `call.` in
  `warning()` and `stop()`. The default `call. = FALSE` follows the
  tidyverse guidance of not displaying the call (#2226, @Bisaloo)

* `sample_int_linter()` for encouraging `sample.int(n, ...)` over
  equivalents like `sample(1:n, ...)` (part of #884, @MichaelChirico).

* `stopifnot_all_linter()` discourages tests with `all()` like
  `stopifnot(all(x > 0))`; `stopifnot()` runs `all()` itself, and
  signals a better error message (part of #884, @MichaelChirico).

* `comparison_negation_linter()` for discouraging negated comparisons
  when a direct negation is preferable, e.g. `!(x == y)` could be `x
  != y` (part of #884, @MichaelChirico).

* `nzchar_linter()` for encouraging `nzchar()` to test for empty
  strings, e.g. `nchar(x) > 0` can be `nzchar(x)` (part of #884,
  @MichaelChirico).

* `terminal_close_linter()` for discouraging using `close()` to end
  functions (part of #884, @MichaelChirico). Such usages are not
  robust to errors, where `close()` will not be run as intended. Put
  `close()` in an `on.exit()` hook, or use {withr} to manage
  connections with proper cleanup.

* `rep_len_linter()` for encouraging use of `rep_len()` directly
  instead of `rep(x, length.out = n)` (part of #884,
  @MichaelChirico). Note that in older versions of R (e.g. pre-4.0),
  `rep_len()` may not copy attributes as expected.

* `which_grepl_linter()` for discouraging `which(grepl(ptn, x))` in
  favor of directly using `grep(ptn, x)` (part of #884,
  @MichaelChirico).

* `list_comparison_linter()` for discouraging comparisons on the
  output of `lapply()`, e.g. `lapply(x, sum) > 10` (part of #884,
  @MichaelChirico).

* `print_linter()` for discouraging usage of `print()` on string
  literals like `print("Reached here")` or `print(paste("Found",
  nrow(DF), "rows."))` (#1894, @MichaelChirico).

* `unnecessary_nesting_linter()` for discouraging overly-nested code
  where an early return or eliminated sub-expression (inside `{`) is
  preferable (#2317, #2334 and part of #884, @MichaelChirico).

* `consecutive_mutate_linter()` for encouraging consecutive calls to
  `dplyr::mutate()` to be combined (part of #884, @MichaelChirico).

* `if_switch_linter()` for encouraging `switch()` over repeated
  `if`/`else` tests (#2322 and part of #884, @MichaelChirico).

* `nested_pipe_linter()` for discouraging pipes within pipes,
  e.g. `df1 %>% inner_join(df2 %>% select(a, b))` (part of #884,
  @MichaelChirico).

* `nrow_subset_linter()` for discouraging usage like `nrow(subset(x,
  conditions))` in favor of something like `with(x, sum(conditions))`
  which doesn't require a full subset of `x` (#2313, #2314 and part of
  #884, @MichaelChirico).

* `pipe_return_linter()` for discouraging usage of `return()` inside a
  {magrittr} pipeline (part of #884, @MichaelChirico).

* `one_call_pipe_linter()` for discouraging one-step pipelines like `x
  |> as.character()` (#2330 and part of #884, @MichaelChirico).

* `object_overwrite_linter()` for discouraging re-use of upstream
  package exports as local variables (#2344, #2346 and part of #884,
  @MichaelChirico and @AshesITR).

### Lint accuracy fixes: removing false positives

* `object_name_linter()` and `object_length_linter()` ignore {rlang}
  name injection like `x |> mutate("{new_name}" := foo(col))` (#1926,
  @MichaelChirico). No checking is applied in such cases. {data.table}
  in-place assignments like `DT[, "sPoNGeBob" := "friend"]` \ 
are still
  eligible for lints.

* `object_usage_linter()` finds global variables assigned with `=` or
  `->`, which avoids some issues around "undefined global variables"
  in scripts (#2654, @MichaelChirico).

## Notes

* `{lintr}` now has a hex sticker
  (https://github.com/rstudio/hex-stickers/pull/110). Thank you,
  @gregswinehart!

* All user-facing messages (including progress bars) are now prepared
  using the `{cli}` package (#2418 and #2641, @IndrajeetPatil). As
  noted above, all messages have been reviewed and updated to be more
  informative and consistent.

* File locations in lints and error messages contain clickable
  hyperlinks to improve code navigation (#2645, #2588, @olivroy).

* {lintr} now depends on R version 4.0.0. It already does so
  implicitly due to recursive upstream dependencies requiring this
  version; we've simply made that dependency explicit and up-front
  (#2569, @MichaelChirico).

* Some code with parameters accepting regular expressions is less
  strict about whether there are capture groups (#2678,
  @MichaelChirico). In particular, this affects
  `unreachable_code_linter(allow_comment_regex=)` and
  `expect_lint(checks=)`.

Files:
RevisionActionfile
1.7modifypkgsrc/devel/R-lintr/Makefile
1.8modifypkgsrc/devel/R-lintr/distinfo