版本发布 5
## Deprecations & breaking changes * 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. Emacs ESS users may need to update to a recent version, e.g. `ESS>20251003`. * Arguments `allow_cascading_assign=`, `allow_right_assign=`, and `allow_pipe_assign=` to `assignment_linter()` are now defunct. * Six linters marked as deprecated with warning in the previous release are now fully deprecated: `consecutive_stopifnot_linter()`, `extraction_operator_linter()`, `no_tab_linter()`, `single_quotes_linter()`, `unnecessary_nested_if_linter()`, and `unneeded_concatenation_linter()`. They will be removed in the next release. * As previously announced, the following fully-deprecated items are now removed from the package: + `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()`. * 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 under 'New and improved features'. * `Lint()`, and thus all linters, require that the returned object's `message` attribute is consistently a simple character string (and not, for example, an object of class `"glue"`; #2740, @MichaelChirico). In general it is good to avoid slower string builders like `glue()` inside a loop (a linter might be run on every expression in your pakcage). Classed character strings return a warning in this release, which will be upgraded to an error subsequently. ## Bug fixes * 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). * `expect_lint()` conforms to {testthat} v3.3.0+ rules for custom expectations, namely that they produce either exactly one success or exactly one failure (#2937, @hadley). ## Changes to default linters * `pipe_consistency_linter()`, with its new default to enforce the native pipe `|>`, is now a default linter, since it corresponds directly to a rule in the Tidyverse Style Guide (#2707, @MichaelChirico). ## New and improved features ### New linters * `all_equal_linter()` warns about incorrect use of `all.equal()` in `if` clauses or preceded by `!` (#2885, @Bisaloo). Such usages should wrap `all.equal()` with `isTRUE()`, for example. * `download_file_linter()` encourages the use of `mode = "wb"` (or `mode = "ab"`) when using `download.file()`, rather than `mode = "w"` or `mode = "a"`, as the latter can produce broken files in Windows (#2882, @Bisaloo). * `list2df_linter()` encourages the use of the `list2DF()` function, or the `data.frame()` function when recycling is required, over the slower and less readable `do.call(cbind.data.frame, )` alternative (#2834, @Bisaloo). * `coalesce_linter()` encourages the use of the infix operator `x %||% y`, which is equivalent to `if (is.null(x)) y else x` (#2246, @MichaelChirico). While this has long been used in many tidyverse packages (it was added to {ggplot2} in 2008), it became part of every R installation from R 4.4.0. Thanks also to @emmanuel-ferdman for fixing a false positive before release. ### Linter improvements * `brace_linter()` has a new argument `function_bodies` (default `"multi_line"`) which controls when to require function bodies to be wrapped in curly braces, with the options `"always"`, `"multi_line"` (only require curly braces when a function body spans multiple lines), `"not_inline"` (only require curly braces when a function body starts on a new line) and `"never"` (#1807, #2240, @salim-b). * `seq_linter()`: + recommends using `seq_along(x)` instead of `seq_len(length(x))` (#2577, @MichaelChirico). + recommends using `sequence()` instead of `unlist(lapply(ints, seq))` (#2618, @Bisaloo). * `undesirable_operator_linter()`: + Lints operators in prefix form, e.g. `` `%%`(x, 2)`` (#1910, @MichaelChirico). Disable this by setting `call_is_undesirable=FALSE`. + Accepts unnamed entries, treating them as undesirable operators, e.g. `undesirable_operator_linter("%%")` (#2536, @MichaelChirico). * `undesirable_function_linter()` accepts unnamed entries, treating them as undesirable functions, e.g. `undesirable_function_linter("sum")` (#2536, @MichaelChirico). * `indentation_linter()` handles un-braced `for` loops correctly (#2564, @MichaelChirico). * Setting `exclusions` supports globs like `knitr*` to exclude files/directories with a pattern (#1554, @MichaelChirico). * `object_name_linter()` and `object_length_linter()` apply to objects assigned with `assign()` or generics created with `setGeneric()` (#1665, @MichaelChirico). * `object_usage_linter()` gains argument `interpret_extensions` to govern which false positive-prone common syntaxes should be checked for used objects (#1472, @MichaelChirico). Currently `"glue"` (renamed from earlier argument `interpret_glue`) and `"rlang"` are supported. The latter newly covers usage of the `.env` pronoun like `.env$key`, where `key` was previously missed as being a used variable. * `boolean_arithmetic_linter()` finds many more cases like `sum(x | y) == 0` where the total of a known-logical vector is compared to 0 (#1580, @MichaelChirico). * `any_duplicated_linter()` is extended to recognize some usages from {dplyr} and {data.table} that could be replaced by `anyDuplicated()`, e.g. `n_distinct(col) == n()` or `uniqueN(col) == .N` (#2482, @MichaelChirico). * `fixed_regex_linter()` recognizes usage of the new (R 4.5.0) `grepv()` wrapper of `grep()`; `regex_subset_linter()` also recommends `grepv()` alternatives (#2855, @MichaelChirico). * `object_usage_linter()` lints missing packages that may cause false positives (#2872, @AshesITR) * `sprintf_linter()` lints `sprintf()` and `gettextf()` calls when a constant string is passed to `fmt` (#2894, @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). * `line_length_linter()` has a new argument `ignore_string_bodies` (defaulting to `FALSE`) which governs whether the contents of multi-line string bodies should be linted (#856, @MichaelChirico). We think the biggest use case for this is writing SQL in R strings, especially in cases where the recommended string width for SQL & R differ. * `package_hooks_linter()` now validates `.onUnload()` hook signatures, requiring exactly one argument starting with 'lib' (#2940, @emmanuel-ferdman). ### Lint accuracy fixes: removing false positives * `unnecessary_nesting_linter()`: + Treats function bodies under the shorthand lambda (`\()`) the same as normal function bodies (#2748, @MichaelChirico). + Treats `=` assignment the same as `<-` when deciding to combine consecutive `if()` clauses (#2245, @MichaelChirico). * `string_boundary_linter()` omits lints of patterns like `\\^` which have an anchor but are not regular expressions (#2636, @MichaelChirico). * `implicit_integer_linter(allow_colon = TRUE)` is OK with negative literals, e.g. `-1:1` or `1:-1` (#2673, @MichaelChirico). * `missing_argument_linter()` allows empty calls like `foo()` even if there are comments between `(` and `)` (#2741, @MichaelChirico). * `return_linter()` works on functions that happen to use braced expressions in their formals (#2616, @MichaelChirico). * `object_name_linter()` and `object_length_linter()` account for S3 class correctly when the generic is assigned with `=` (#2507, @MichaelChirico). * `assignment_linter()` with `operator = "="` does a better job of skipping implicit assignments, which are intended to be governed by `implicit_assignment_linter()` (#2765, @MichaelChirico). * `implicit_assignment_linter()` with `allow_scoped=TRUE` doesn't lint for `if (a <- 1) print(a)` (#2913, @MichaelChirico). * `expect_true_false_linter()` is pipe-aware, so that `42 |> expect_identical(x, ignore_attr = TRUE)` no longer lints (#1520, @MichaelChirico). * `T_and_F_symbol_linter()` ignores `T` and `F`: + When used as symbols in formulas (`y ~ T + F`), which can represent variables in data not controlled by the author (#2637, @MichaelChirico). + If followed by `[` or `[[` (#2944, @mcol). ### Lint accuracy fixes: removing false negatives * `todo_comment_linter()` finds comments inside {roxygen2} markup comments (#2447, @MichaelChirico). * Linters with logic around function declarations consistently include the R 4.0.0 shorthand `\()` (#2818, continuation of earlier #2190, @MichaelChirico). + `library_call_linter()` + `terminal_close_linter()` + `unnecessary_lambda_linter()` * More consistency on handling `@` extractions to match how similar `$` extractions would be linted (#2820, @MichaelChirico). + `function_left_parentheses_linter()` + `indentation_linter()` + `library_call_linter()` + `missing_argument_linter()` * `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()`. ### Other improvements * `get_source_expression()` captures warnings emitted by the R parser (currently always for mis-specified literal integers like `1.1L`) and `lint()` returns them as lints (#2065, @MichaelChirico). * `expect_lint()` has a new argument `ignore_order` (default `FALSE`), which, if `TRUE`, allows the `checks=` to be provided in arbitary order vs. how `lint()` produces them (@MichaelChirico). * New `gitlab_output()` function to output lints to GitLab format (#2858, @lschneiderbauer). * New argument `include_s4_slots` for the `xml_find_function_calls()` entry in the `get_source_expressions()` to govern whether calls of the form `s4Obj@fun()` are included in the result (#2820, @MichaelChirico). * `use_lintr()` adds the created `.lintr` file to the `.Rbuildignore` if run in a package (#2926, initial work by @MEO265, finalized by @Bisaloo). ## Notes * `{lintr}` now has an associated paper at the [Journal of Open Source Software](https://doi.org/10.21105/joss.07240) that you can use to cite the package if you use it in a paper - see citation("lintr") for details. * `expect_lint_free()` and other functions that rely on the {testthat} framework now have a consistent error message. (#2585, @F-Noelle). * `unnecessary_nesting_linter()` gives a more specific lint message identifying: + the unmatched "exit call" that prompts the recommendation to reduce nesting (#2316, @MichaelChirico). + the specific `if()` statement that can be combined with the linted one (#1891, @MichaelChirico). * The description in `?paste_linter` of `allow_file_path=` has been corrected (#2675, @MichaelChirico). In particular, `allow_file_path="never"` is the most strict form, `allow_file_path="always"` is the most lax form. * `comment_token` is removed from settings. This was a vestige of the now-defunct support for posting GitHub comments. ## New Contributors * @lschneiderbauer made their first contribution in https://github.com/r-lib/lintr/pull/2862 * @marberts made their first contribution in https://github.com/r-lib/lintr/pull/2924 * @emmanuel-ferdman made their first contribution in https://github.com/r-lib/lintr/pull/2938 * @mcol made their first contribution in https://github.com/r-lib/lintr/pull/2942 * @TimTaylor made their first contribution in https://github.com/r-lib/lintr/pull/2962 * @clerousset made their first contribution in https://github.com/r-lib/lintr/pull/2961 **Full Changelog**: https://github.com/r-lib/lintr/compare/v3.2.0...v3.3.0-1
## 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=)`. ## New Contributors * @jonthegeek made their first contribution in https://github.com/r-lib/lintr/pull/2533 * @F-Noelle made their first contribution in https://github.com/r-lib/lintr/pull/2574 * @olivroy made their first contribution in https://github.com/r-lib/lintr/pull/2602 * @jcken95 made their first contribution in https://github.com/r-lib/lintr/pull/2672 * @etiennebacher made their first contribution in https://github.com/r-lib/lintr/pull/2697 **Full Changelog**: https://github.com/r-lib/lintr/compare/v3.1.1...v3.2.0
# New and improved features ## Lint accuracy fixes: removing false positives * `unreachable_code_linter()` ignores reachable code in inline functions like `function(x) if (x > 2) stop() else x` (#2259, @MEO265). * `unnecessary_lambda_linter()` + ignores extractions with explicit returns like `lapply(l, function(x) foo(x)$bar)` (#2258, @MichaelChirico). + ignores calls on the RHS of operators like `lapply(l, function(x) "a" %in% names(x))` (#2310, @MichaelChirico). * `vector_logic_linter()` recognizes some cases where bitwise `&`/`|` are used correctly (#1453, @MichaelChirico). * `expect_comparison_linter()` ignores faulty usage like `expect_true(x, y > z)` (#2083, @MichaelChirico). Note that `y > z` is being passed to the `info=` argument, so this is likely a mistake. * `consecutive_assertion_linter()` ignores cases where a second assertion follows an intervening assignment with `=` (#2444, @MichaelChirico). ## Lint accuracy fixes: removing false negatives * `missing_argument_linter()` catches all missing arguments in calls with several, e.g. `foo(,,)` gives 3 lints instead of 2 (#2399, @MichaelChirico). * `duplicate_argument_linter()` no longer misses cases with duplicate arguments where a comment comes between the argument name and `=` (#2402, @MichaelChirico). # Notes * Fixed a test assuming a specific parser error message that recently changed in r-devel (#2527, @IndrajeetPatil). * @MichaelChirico has taken over CRAN maintainer duties for the package. Many thanks to @jimhester for more than 10 years and 15 releases wearing that hat!!
## Breaking changes * `infix_spaces_linter()` distinguishes `<-`, `:=`, `<<-` and `->`, `->>`, i.e. `infix_spaces_linter(exclude_operators = "->")` will no longer exclude `->>` (#2115, @MichaelChirico). This change is breaking for users relying on manually-supplied `exclude_operators` containing `"<-"` to also exclude `:=` and `<<-`. The fix is to manually supply `":="` and `"<<-"` as well. We don't expect this change to affect many users, the fix is simple, and the new behavior is much more transparent, so we are including this breakage in a minor release. * Removed `find_line()` and `find_column()` entries from `get_source_expressions()` expression-level objects. These have been marked deprecated since version 3.0.0. No users were found on GitHub. * There is experimental support for writing config in plain R scripts (as opposed to DCF files; #1210, @MichaelChirico). The script is run in a new environment and variables matching settings (`?default_settings`) are copied over. In particular, this removes the need to write R code in a DCF-friendly way, and allows normal R syntax highlighting in the saved file. We may eventually deprecate the DCF approach in favor of this one; user feedback is welcome on strong preferences for either approach, or for a different approach like YAML. Generally you should be able to convert your existing `.lintr` file to an equivalent R config by replacing the `:` key-value separators with assignments (`<-`). By default, such a config is searched for in a file named '.lintr.R'. This is a mildly breaking change if you happened to be keeping a file '.lintr.R' around since that file is given precedence over '.lintr'. + We also validate config files up-front make it clearer when invalid configs are present (#2195, @MichaelChirico). There is a warning for "invalid" settings, i.e., settings not part of `?default_settings`. We think this is more likely to affect users declaring settings in R, since any variable defined in the config that's not a setting must be removed to make it clearer which variables are settings vs. ancillary. ## Bug fixes * `sprintf_linter()` doesn't error in cases where whitespace in `...` arguments is significant, e.g. `sprintf("%s", if (A) "" else y)`, which won't parse if whitespace is removed (#2131, @MichaelChirico). ## Changes to default linters * `assignment_linter()` lints the {magrittr} assignment pipe `%<>%` (#2008, @MichaelChirico). This can be deactivated by setting the new argument `allow_pipe_assign` to `TRUE`. * `object_usage_linter()`: + assumes `glue()` is `glue::glue()` when `interpret_glue=TRUE` (#2032, @MichaelChirico). + finds function usages, including infix usage, inside `glue()` calls to avoid false positives for "unused objects" (#2029 and #2069, @MichaelChirico). * `object_name_linter()` no longer attempts to lint strings in function calls on the LHS of assignments (#1466, @MichaelChirico). * `infix_spaces_linter()` allows finer control for linting `=` in different scenarios using parse tags `EQ_ASSIGN`, `EQ_SUB`, and `EQ_FORMALS` (#1977, @MichaelChirico). * `equals_na_linter()` checks for `x %in% NA`, which is a more convoluted form of `is.na(x)` (#2088, @MichaelChirico). ## New and improved features * 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"`. * New `xp_call_name()` helper to facilitate writing custom linters (#2023, @MichaelChirico). This helper converts a matched XPath to the R function to which it corresponds. This is useful for including the "offending" function in the lint's message. * New `make_linter_from_xpath()` to facilitate making simple linters directly from a single XPath (#2064, @MichaelChirico). This is especially helpful for making on-the-fly/exploratory linters, but also extends to any case where the linter can be fully defined from a static lint message and single XPath. * Toggle lint progress indicators with argument `show_progress` to `lint_dir()` and `lint_package()` (#972, @MichaelChirico). The default is still to show progress in `interactive()` sessions. Progress is also now shown with a "proper" progress bar (`utils::txtProgressBar()`), which in particular solves the issue of progress `.` spilling well past the width of the screen in large directories. * `lint()`, `lint_dir()`, and `lint_package()` fail more gracefully when the user mis-spells an argument name (#2134, @MichaelChirico). * Quarto files (.qmd) are included by `lint_dir()` by default (#2150, @dave-lovell). ### New linters * `library_call_linter()` can detect if all library/require calls are not at the top of your script (#2027, #2043, #2163, and #2170, @nicholas-masel and @MichaelChirico). * `keyword_quote_linter()` for finding unnecessary or discouraged quoting of symbols in assignment, function arguments, or extraction (part of #884, @MichaelChirico). Quoting is unnecessary when the target is a valid R name, e.g. `c("a" = 1)` can be `c(a = 1)`. The same goes to assignment (`"a" <- 1`) and extraction (`x$"a"`). Where quoting is necessary, the linter encourages doing so with backticks (e.g. `` x$`a b` `` instead of `x$"a b"`). * `length_levels_linter()` for using the specific function `nlevels()` instead of checking `length(levels(x))` (part of #884, @MichaelChirico). * `scalar_in_linter()` for discouraging `%in%` when the right-hand side is a scalar, e.g. `x %in% 1` (part of #884, @MichaelChirico). * `if_not_else_linter()` for encouraging `if` statements to be structured as `if (A) x else y` instead of `if (!A) y else x` (part of #884, @MichaelChirico). * `repeat_linter()` for encouraging `repeat` for infinite loops instead of `while (TRUE)` (#2106, @MEO265). * `length_test_linter()` detects the common mistake `length(x == 0)` which is meant to be `length(x) == 0` (#1991, @MichaelChirico). ### Extensions to existing linters * `fixed_regex_linter()` gains an option `allow_unescaped` (default `FALSE`) to toggle linting regexes not requiring any escapes or character classes (#1689, @MichaelChirico). Thus `fixed_regex_linter(allow_unescaped = TRUE)` would lint on `grepl("[$]", x)` but not on `grepl("a", x)` since the latter does not use any regex special characters. * `line_length_linter()` helpfully includes the line length in the lint message (#2057, @MichaelChirico). * `conjunct_test_linter()` also lints usage like `dplyr::filter(x, A & B)` in favor of using `dplyr::filter(x, A, B)` (part of #884; #2110 and #2078, @salim-b and @MichaelChirico). Option `allow_filter` toggles when this applies. `allow_filter = "always"` drops such lints entirely, while `"not_dplyr"` only lints calls explicitly qualified as `dplyr::filter()`. The default, `"never"`, assumes all unqualified calls to `filter()` are `dplyr::filter()`. * `sort_linter()` checks for code like `x == sort(x)` which is better served by using the function `is.unsorted()` (part of #884, @MichaelChirico). * `paste_linter()` gains detection for file paths that are better constructed with `file.path()`, e.g. `paste0(dir, "/", file)` would be better as `file.path(dir, file)` (part of #884, #2082, @MichaelChirico). What exactly gets linted here can be fine-tuned with the `allow_file_path` option (`"double_slash"` by default, with alternatives `"never"` and `"always"`). When `"always"`, these rules are ignored. When `"double_slash"`, paths appearing to construct a URL that have consecutive forward slashes (`/`) are skipped. When `"never"`, even URLs should be constructed with `file.path()`. * `seq_linter()` recommends `rev()` in the lint message for lints like `nrow(x):1` (#1542, @MichaelChirico). * `function_argument_linter()` detects usage of `missing()` for the linted argument (#1546, @MichaelChirico). The simplest fix for `function_argument_linter()` lints is typically to set that argument to `NULL` by default, in which case it's usually preferable to update function logic checking `missing()` to check `is.null()` instead. * `commas_linter()` gains an option `allow_trailing` (default `FALSE`) to allow trailing commas while indexing. (#2104, @MEO265) * `unreachable_code_linter()` + checks for code inside `if (FALSE)` and other conditional loops with deterministically false conditions (#1428, @ME0265). + checks for unreachable code inside `if`, `else`, `for`, `while`, and `repeat` blocks, including combinations with `break` and `next` statements. (#2105, @ME0265). * `implicit_assignment_linter()` gains an argument `allow_lazy` (default `FALSE`) that allows optionally skipping lazy assignments like `A && (B <- foo(A))` (#2016, @MichaelChirico). * `unused_import_linter()` gains an argument `interpret_glue` (default `TRUE`) paralleling that in `object_usage_linter()` to toggle whether `glue::glue()` expressions should be inspected for exported object usage (#2042, @MichaelChirico). * `default_undesirable_functions` is updated to also include `Sys.unsetenv()` and `structure()` (#2192 and #2228, @IndrajeetPatil and @MichaelChirico). * Linters with logic around the magrittr pipe `%>%` consistently apply it to the other pipes `%!>%`, `%T>%`, `%<>%` (and possibly `%$%`) where appropriate (#2008, @MichaelChirico). + `brace_linter()` + `pipe_call_linter()` + `pipe_continuation_linter()` + `unnecessary_concatenation_linter()` + `unnecessary_placeholder_linter()` * Linters with logic around function declarations consistently include the R 4.0.0 shorthand `\()` (#2190, @MichaelChirico). + `brace_linter()` + `function_left_parentheses_linter()` + `indentation_linter()` + `object_length_linter()` + `object_name_linter()` + `package_hooks_linter()` + `paren_body_linter()` + `unnecessary_lambda_linter()` + `unreachable_code_linter()` ### Lint accuracy fixes: removing false positives * `fixed_regex_linter()` + Is pipe-aware, in particular removing false positives around piping into {stringr} functions like `x |> str_replace(fixed("a"), "b")` (#1811, @MichaelChirico). + Ignores non-string inputs to `pattern=` as a keyword argument (#2159, @MichaelChirico). * Several linters avoiding false positives in `$` extractions get the same exceptions for `@` extractions, e.g. `S4@T` will no longer throw a `T_and_F_symbol_linter()` hit (#2039, @MichaelChirico). + `T_and_F_symbol_linter()` + `for_loop_index_linter()` + `literal_coercion_linter()` + `object_name_linter()` + `undesirable_function_linter()` + `unreachable_code_linter()` + `yoda_test_linter()` * `sprintf_linter()` is pipe-aware, so that `x %>% sprintf(fmt = "%s")` no longer lints (#1943, @MichaelChirico). * `condition_message_linter()` ignores usages of extracted calls like `env$stop(paste(a, b))` (#1455, @MichaelChirico). * `inner_combine_linter()` no longer throws on length-1 calls to `c()` like `c(exp(2))` or `c(log(3))` (#2017, @MichaelChirico). Such usage is discouraged by `unnecessary_concatenation_linter()`, but `inner_combine_linter()` _per se_ does not apply. * `sort_linter()` only lints on `order()` of a single vector, excluding e.g. `x[order(x, y)]` and `x[order(y, x)]` (#2156, @MichaelChirico). * `redundant_ifelse_linter()` is aware of `dplyr::if_else()`'s `missing=` argument, so that `if_else(A, TRUE, FALSE, missing = FALSE)` doesn't lint, but `if_else(A, TRUE, FALSE, NA)` does (#1941, @MichaelChirico). Note that `dplyr::coalesce()` or `tidyr::replace_na()` may still be preferable. ### Lint accuracy fixes: removing false negatives * `unreachable_code_linter()` finds unreachable code even in the presence of a comment or semicolon after `return()` or `stop()` (#2127, @MEO265). * `implicit_assignment_linter()` + finds assignments in call arguments besides the first one (#2136, @MichaelChirico). + finds assignments in parenthetical expressions like `if (A && (B <- foo(A))) { }` (#2138, @MichaelChirico). * `unnecessary_lambda_linter()` checks for cases using explicit returns, e.g. `lapply(x, \(xi) return(sum(xi)))` (#1567, @MichaelChirico). + thanks to @Bisaloo and @strengejacke for detecting a regression in the original fix (#2231, #2247).
## Deprecations & Breaking Changes * `.lintr` files can now be kept in the directory `.github/linters` for better compatibility with Super-Linter. Note that this may be a breaking change if you already have a config in `.github/linters` inside a subdirectory as well as in your R project's root, since the former will now be discovered first where it was ignored before. Please see `vignette("lintr")` for details on how configs are discovered (#1746, @tonyk7440 and @klmr). * `single_quotes_linter()` is deprecated in favor of the more generalizable `quotes_linter()` (#1729, @MichaelChirico). * `unneeded_concatentation_linter()` is deprecated in favor of `unnecessary_concatenation_linter()` for naming consistency (#1707, @IndrajeetPatil). * `consecutive_stopifnot_linter()` is deprecated in favor of the more general (see below) `consecutive_assertion_linter()` (#1604, @MichaelChirico). * `no_tab_linter()` is deprecated in favor of `whitespace_linter()` for naming consistency and future generalization (#1954, @MichaelChirico). * `available_linters()` prioritizes `tags` over `exclude_tags` in the case of overlap, i.e., tags listed in both arguments are included, not excluded. We don't expect many people to be affected by this, and the old behavior was not made explicit in the documentation, but make note of it here since it required changing a test in lintr's own suite where `linters_with_tags()` implicitly assumed this behavior. * `lint()`, `lint_dir()`, and `lint_package()` no longer accept certain arguments (`cache=` for `lint()`, `relative_path=` for the latter two) positionally. The `warning()` since 3.0.0 has been upgraded to an error. ## Bug fixes * `linters_with_tags()` now includes the previously missing spaces around "and" when listing missing linters advertised by `available_linters()`. This error message may appear e.g. when you update lintr to a version with new linters but don't restart your R session (#1946, @Bisaloo) * `fixed_regex_linter()` is more robust to errors stemming from unrecognized escapes (#1545, #1845, @IndrajeetPatil). * `get_source_expressions()` can handle Sweave/Rmarkdown documents with reference chunks like `<<ref_file>>` (#779, @MichaelChirico). Note that these are simply skipped, rather than attempting to retrieve the reference and also lint it. * `assignment_linter()` no longer lints assignments in braces that include comments when `allow_trailing = FALSE` (#1701, @ashbaldry) * `object_usage_linter()` + No longer silently ignores usage warnings that don't contain a quoted name (#1714, @AshesITR) + No longer fails on code with comments inside a multi-line call to `glue::glue()` (#1919, @MichaelChirico) * `namespace_linter()` correctly recognizes backticked operators to be exported from respective namespaces (like `` rlang::`%||%` ``) (#1752, @IndrajeetPatil) * `lint_package()` correctly finds a package from within a subdir if the `path` points to anywhere within the package (#1759, @AshesITR) * Improved error behavior in `Lint()`, `lint()` and `xml_nodes_to_lints()` (#1427, #763, @AshesITR) + `Lint()` validates its inputs more thoroughly, preventing errors during `print.Lints` like "Error in rep.int(character, length) : invalid 'times' value:". + `lint()` no longer tries to create an expression tree with unexpected end of input errors, because they can be broken. + `xml_nodes_to_lints()` warns if it can't find lint locations and uses dummy locations as a fallback. * `linters_with_defaults()` no longer erroneously marks linter factories as linters (#1725, @AshesITR). * Row names for `available_linters()` data frame are now contiguous (#1781, @IndrajeetPatil). * `object_name_linter()` allows all S3 group Generics (see `?base::groupGeneric`) and S3 generics defined in a different file in the same package (#1808, #1841, @AshesITR) * `object_usage_linter()` improves identification of the exact source of a lint + for undefined variables in expressions with where the variable is used as a symbol in a usual way, for example in a formula or in an extraction with `$` (#1914, @MichaelChirico). + for general usage warnings without location info (#1986 and #1917, @AshesITR) * `function_left_parentheses_linter()` produces a more specific lint (and no longer fails) when the opening parenthesis is on a different line than `function` or the call name (#1953, @MichaelChirico). Thanks also to @IndrajeetPatil and @lorenzwalthert for identifying a regression in the initial fix, #1963. ## Changes to defaults * Set the default for the `except` argument in `duplicate_argument_linter()` to `c("mutate", "transmute")`. This allows sequential updates like `x |> mutate(a = b + 1, a = log(a))` (#1345, @IndrajeetPatil). * `object_usage_linter()` + gains `skip_with` argument to skip code in `with()` expressions. To be consistent with `R CMD check`, it defaults to `TRUE` (#941, #1458, @IndrajeetPatil). + Handles backticked symbols inside {glue} expressions correctly, e.g. ``glue("{`x`}")`` correctly determines `x` was used (#1619, @MichaelChirico) + Detects problems inside R4.1.0+ lambda functions (`\(...)`) (#1933, @MichaelChirico) * `spaces_inside_linter()` allows terminal missing keyword arguments (e.g. `alist(arg = )`; #540, @MichaelChirico) * `brace_linter()` allows empty braced expression on the same line (e.g. `while (updating_condition()) { }`) regardless of `allow_single_line` to match the corresponding behavior in {styler}. This is an expedient while the style guide on handling this case awaits clarification: https://github.com/tidyverse/style/issues/191. (#1346, @MichaelChirico) * `undesirable_function_linter()` and `undesirable_operator_linter()` now produce an error if empty vector of undesirable functions or operators is provided (#1867, @IndrajeetPatil). * New linters which are also included as defaults (see "New linters" for more details): + `indentation_linter()` + `quotes_linter()` + `unnecessary_concatenation_linter()` + `whitespace_linter()` * `lint_package()` also looks for files in `exec/` (#1950, @jmaspons). ## New and improved features * New `get_r_string()` helper to get the R-equivalent value of a string, especially useful for R-4-style raw strings. Previously an internal `lintr` helper, now exported to facilitate writing custom linters (#1493, @MichaelChirico). * `object_usage_linter()` improves lint metadata when detecting undefined infix operators, e.g. `%>%` or `:=` (#1497, @MichaelChirico) * `unused_import_linter()` can detect datasets from imported packages and no longer warns when a package is imported only for its datasets (#1545, @IndrajeetPatil). * When a linter triggers an error, `lint()` will provide a more actionable summary of where the error occurred, particularly useful for cases like `lint_package()` where both the responsible file and the responsible linter would be unknown (@MichaelChirico). Typically, linters should not themselves cause R to stop -- syntax errors lead to error lints, for example. Please report such failures as they are likely bugs. * `pipe_continuation_linter()` recognizes violations involving the native R pipe `|>` (#1609, @MichaelChirico) * `paste_linter()` also catches usages like `paste(rep("*", 10L), collapse = "")` that can be written more concisely as `strrep("*", 10L)` (#1108, @MichaelChirico) * `spaces_inside_linter()` produces lints for spaces inside `[[` (#1673, @IndrajeetPatil). * `sprintf_linter()` also applies to `gettextf()` (#1677, @MichaelChirico) * Documentation for all linters contains examples of code that does and does not produce lints (#1492, @IndrajeetPatil). * `implicit_integer_linter()` gains parameter `allow_colon` to skip lints on expressions like `1:10` (#1155, @MichaelChirico) * `infix_spaces_linter()` supports the native R pipe `|>` (#1793, @AshesITR) * `unnecessary_concatenation_linter()` (f.k.a. `unneeded_concatenation_linter()`) no longer lints on `c(...)` (i.e., passing `...` in a function call) when `allow_single_expression = FALSE` (#1696, @MichaelChirico) * `object_name_linter()` gains parameter `regexes` to allow custom naming conventions (#822, #1421, @AshesITR) * `literal_coercion_linter()` reports a replacement in the lint message, e.g. code like `as.integer(1)` will suggest using `1L` instead, and code like `as.numeric(NA)` will suggest using `NA_real_` instead (#1439, @MichaelChirico) * Added `format()` functions for `lint` and `lints` (#1784, @AshesITR) * `all_linters()` function provides an easy way to access all available linters (#1843, @IndrajeetPatil) * `missing_argument_linter()` allows missing arguments in `quote()` calls (#1889, @IndrajeetPatil). * `get_source_expressions()` correctly extracts indented code chunks from R Markdown documents, which helps avoid spurious lints related to whitespace (#1945, @MichaelChirico). The convention taken is that, within each chunk, all code is anchored relative to the leftmost non-whitespace column. * `available_linters()` gives priority to `tags` over `exclude_tags` in the case of overlap. In particular, this means that `available_linters(tags = "deprecated")` will work to return deprecated linters without needing to specify `exclude_tags` (#1959, @MichaelChirico). * The {lintr} configuration file is now searched in the system's user configuration path; the lintr config filename can also be configured explicitly by setting the environment variable `R_LINTR_LINTER_FILE` (#460, @klmr) * Errors in the {lintr} configuration file now produce more informative error messages (#886, @AshesITR) ### New linters * `matrix_apply_linter()` recommends use of dedicated `rowSums()`, `colSums()`, `colMeans()`, `rowMeans()` over `apply(., MARGIN, sum)` or `apply(., MARGIN, mean)`. The recommended alternative is much more efficient and more readable (#1869, @Bisaloo). * `unnecessary_lambda_linter()`: detect unnecessary lambdas (anonymous functions), e.g. `lapply(x, function(xi) sum(xi))` can be `lapply(x, sum)` and `purrr::map(x, ~quantile(.x, 0.75, na.rm = TRUE))` can be `purrr::map(x, quantile, 0.75, na.rm = TRUE)`. Naming `probs = 0.75` can further improve readability (#1531, #1866, @MichaelChirico, @Bisaloo). * `redundant_equals_linter()` for redundant comparisons to `TRUE` or `FALSE` like `is_treatment == TRUE` (#1500, @MichaelChirico) * `lengths_linter()` for encouraging usage of `lengths(x)` instead of `sapply(x, length)` (and similar) * `function_return_linter()` for handling issues in function `return()` statements. Currently handles assignments within the `return()` clause, e.g. `return(x <- foo())` (@MichaelChirico) * `boolean_arithmetic_linter()` for identifying places where logical aggregations are more appropriate, e.g. `length(which(x == y)) == 0` is the same as `!any(x == y)` or even `all(x != y)` (@MichaelChirico) * `for_loop_index_linter()` to prevent overwriting local variables in a `for` loop declared like `for (x in x) { ... }` (@MichaelChirico) * `is_numeric_linter()` for redundant checks equivalent to `is.numeric(x)` such as `is.numeric(x) || is.integer(x)` or `class(x) %in% c("numeric", "integer")` (@MichaelChirico) * `empty_assignment_linter()` for identifying empty assignments like `x = {}` that are more clearly written as `x = NULL` (@MichaelChirico) * `unnecessary_placeholder_linter()` for identifying where usage of the {magrittr} placeholder `.` could be omitted (@MichaelChirico) * `routine_registration_linter()` for identifying native routines that don't use registration (`useDynLib` in the `NAMESPACE`; @MichaelChirico) * `indentation_linter()` for checking that the indentation conforms to 2-space Tidyverse-style (@AshesITR and @dgkf, #1411, #1792, #1898). * `unnecessary_nested_if_linter()` for checking unnecessary nested `if` statements where a single `if` statement with appropriate conditional expression would suffice (@IndrajeetPatil and @AshesITR, #1778). * `implicit_assignment_linter()` for checking implicit assignments in function calls (@IndrajeetPatil and @AshesITR, #1777). * `quotes_linter()` is a generalized version of (now deprecated) `single_quotes_linter()`. It accepts an argument `delimiter` to specify whether `"` or `'` should be the accepted method for delimiting character literals. The default, `"`, reflects the Tidyverse style guide recommendation and matches the behavior of `single_quotes_linter()`. * `unnecessary_concatenation_linter()` is simply `unneeded_concatenation_linter()`, renamed. * `consecutive_assertion_linter()` (f.k.a. `consecutive_stopifnot_linter()`) now lints for consecutive calls to `assertthat::assert_that()` (as long as the `msg=` argument is not used; #1604, @MichaelChirico). * `whitespace_linter()` is simply `no_tab_linter()`, renamed. In the future, we plan to extend it to work for different whitespace preferences. ## Notes * {lintr} now depends on R version 3.5.0, in line with the tidyverse policy for R version compatibility. * `lint()` continues to support Rmarkdown documents. For users of custom .Rmd engines, e.g. `marginformat` from {tufte} or `theorem` from {bookdown}, note that those engines must be registered in {knitr} prior to running `lint()` in order for {lintr} to behave as expected, i.e., they should be shown as part of `knitr::knit_engines$get()`. For {tufte} and {bookdown} in particular, one only needs to load the package namespace to accomplish this (i.e., minimally `loadNamespace("tufte")` or `loadNamespace("bookdown")`, respectively, will register those packages' custom engines; since `library()` also runs `loadNamespace()`, running `library()` will also work). Note further that {tufte} only added this code to their `.onLoad()` recently after our request to do so (see https://github.com/rstudio/tufte/issues/117). Therefore, ensure you're using a more recent version to get the behavior described here for {tufte}. More generally, there is no requirement that `loadNamespace()` will register a package's custom {knitr} engines, so you may need to work with other package authors to figure out a solution for other engines. Thanks to Yihui and other developers for their helpful discussions around this issue (#797, @IndrajeetPatil). * The output of `lint()` and `Lint()` gain S3 class `"list"` to assist with S3 dispatch (#1494, @MichaelChirico) + As a corollary, we now register an `as_tibble` method for class `lints`, conditional on {tibble} availability, to avoid dispatching to the `list` method which does not work with `lint()` output (#1997, @MichaelChirico) * `object_usage_linter()` gives a more helpful warning when a `glue()` expression fails to evaluate (#1985, @MichaelChirico) * The documentation of `object_name_linter()` now describes how `"symbols"` works when passed to the `styles` parameter (#1924, @hedsnz). ## What's Changed * Bump to devel by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1536 * GHA workflow `lint-changed-files` fails when a new lint is found in changed files by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1522 * `object_usage_linter()` sets `useFancyQuotes=FALSE` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1523 * move NEWS item about sarif_output() to the correct release by @MichaelChirico in https://github.com/r-lib/lintr/pull/1537 * Edits to docs for linters by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1480 * Get rid of version label and tooltip by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1534 * Remove TODOs related to `{waldo}` update by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1518 * Collapse near-identical sections in NEWS of 3.0.1 by @MichaelChirico in https://github.com/r-lib/lintr/pull/1538 * Formatting changes to follow tidyverse style guide by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1539 * `fixed_regex_linter()` doesn't fail with `"\\;"` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1547 * `unused_import_linter()` detects datasets from imports by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1549 * object_usage_linter gets correct metadata for infix operators by @MichaelChirico in https://github.com/r-lib/lintr/pull/1525 * Slightly improve dataset symbol detection by @MichaelChirico in https://github.com/r-lib/lintr/pull/1555 * Export get_r_string by @MichaelChirico in https://github.com/r-lib/lintr/pull/1526 * Include `expect_*()` linters in `.lintr_new` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1557 * Add arg to skip `with()` in `object_usage_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1548 * Add exceptions for `duplicate_argument_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1550 * Check with the oldest supported R version by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1551 * remove expect_identical_linter() from .lintr_new, which we don't use by @MichaelChirico in https://github.com/r-lib/lintr/pull/1561 * use 4.1.0 for consistency by @MichaelChirico in https://github.com/r-lib/lintr/pull/1562 * Remove `{covr}` unneeded from `Suggests` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1560 * Minor header formatting changes for old releases by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1563 * Add R-CMD-check with non-english locale by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1564 * New redundant_equals_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1556 * Sync up with lint workflow in `r-lib/actions` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1559 * Update docs and adds tests for Quarto files by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1487 * New unnecessary_lambda_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1541 * Examples for ?expect_lint too wide for R CMD check by @MichaelChirico in https://github.com/r-lib/lintr/pull/1571 * New `function_return_linter()` by @MichaelChirico in https://github.com/r-lib/lintr/pull/1569 * Input validation tests for `available_linters()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1572 * Create package Rd file by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1577 * Add missing test to `function_return_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1578 * New lengths_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1568 * Improve docs about how to list and use all linters by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1576 * switch away from //expr in brace_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1585 * switch away from //expr XPaths in paste_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1586 * Move away from //expr XPaths in object_usage_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1589 * Move away from //expr XPaths in string_boundary_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1588 * switch away from //expr XPaths in package_hooks_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1587 * Move more XPaths away from //expr logic by @MichaelChirico in https://github.com/r-lib/lintr/pull/1590 * New boolean_arithmetic_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1579 * Include more linters in `.lintr_new` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1593 * Update docs for `extraction_operator_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1592 * Another batch of migrations away from //expr by @MichaelChirico in https://github.com/r-lib/lintr/pull/1594 * friendlier failure of lint() by @MichaelChirico in https://github.com/r-lib/lintr/pull/1583 * redundant equals by @MichaelChirico in https://github.com/r-lib/lintr/pull/1595 * Add examples to documentation: Part-1 (a-b) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1591 * Get rid of a few lints by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1596 * Follow style guide in a few test files by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1598 * Finish styling remaining test files by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1600 * Improve resilience of object_usage_linter() to glue syntax issues by @MichaelChirico in https://github.com/r-lib/lintr/pull/1597 * Move deprecated linters to one file and hide them on website by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1602 * More XPaths avoiding //expr by @MichaelChirico in https://github.com/r-lib/lintr/pull/1605 * Complete switch away from //expr XPaths by @MichaelChirico in https://github.com/r-lib/lintr/pull/1606 * Add examples to documentation: Part-2 (c-d) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1603 * switch to using a hanging indent for extended XPaths by @MichaelChirico in https://github.com/r-lib/lintr/pull/1610 * Turn on codecov comments by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1615 * Add examples to the docs for utilities by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1618 * Error on a `NOTE` in R CMD check by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1621 * improve handling of glued object extraction by @MichaelChirico in https://github.com/r-lib/lintr/pull/1612 * Always qualify rex in tests, make private API calls clearer by @MichaelChirico in https://github.com/r-lib/lintr/pull/1628 * Add nocov directives to Rd fragment functions by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1627 * Add more missing tests by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1624 * Add a test for `sarif_output()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1623 * Anticipate more knitr engines in code blocks by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1552 * Tests for `trailing_blank_lines_linter()` with chunks by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1614 * New for_loop_index_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1629 * Add a few more linters to "lints changed" workflow by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1632 * add examples to get_r_string by @MichaelChirico in https://github.com/r-lib/lintr/pull/1640 * Handle <<reference_chunks>> in Rnw,Rmd by @MichaelChirico in https://github.com/r-lib/lintr/pull/1642 * Add "list" to S3 class of lint(), Lint() output by @MichaelChirico in https://github.com/r-lib/lintr/pull/1641 * handle alist(kwd = ) false positive by @MichaelChirico in https://github.com/r-lib/lintr/pull/1643 * New empty_assignment_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1637 * Add examples to documentation: Part-3 (e) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1631 * Recognize |> in pipe_continuation_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1638 * Reformat lint example by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1644 * nocov for unanticipated error by @MichaelChirico in https://github.com/r-lib/lintr/pull/1647 * Use {withr} more comprehensively in tests to manage state by @MichaelChirico in https://github.com/r-lib/lintr/pull/1646 * Additional test for `namespace_linter()`+ clean-up by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1649 * Document default settings by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1648 * New is_numeric_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1635 * Add examples to documentation: Part-4 (f-i) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1651 * Remove code to deal with broken namespaces by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1653 * extend paste_linter() for strrep() equivalents by @MichaelChirico in https://github.com/r-lib/lintr/pull/1652 * Consistently use `lint_msg` instead of `msg` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1654 * Add examples to documentation: Part-5 (l-n) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1655 * nocov version-specific region by @MichaelChirico in https://github.com/r-lib/lintr/pull/1659 * coverage test of xml_nodes_to_lints by @MichaelChirico in https://github.com/r-lib/lintr/pull/1658 * New unnecessary_placeholder_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1656 * small simplification for glue, add coverage test by @MichaelChirico in https://github.com/r-lib/lintr/pull/1660 * nocov unreachable helper by @MichaelChirico in https://github.com/r-lib/lintr/pull/1663 * Add examples to documentation: Part-6 (o-p) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1664 * friendlier input validation, coverage in undesirable_operator_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1666 * nocov an unreachable error by @MichaelChirico in https://github.com/r-lib/lintr/pull/1667 * jsonlite is a Suggests dependency by @MichaelChirico in https://github.com/r-lib/lintr/pull/1670 * New routine_registration_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1669 * crayon is a Suggested dependency by @MichaelChirico in https://github.com/r-lib/lintr/pull/1671 * nocov missing crayon branch by @MichaelChirico in https://github.com/r-lib/lintr/pull/1676 * Add examples to documentation: Part-7 (q-s) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1674 * `spaces_inside_linter()` lints spaces after `[[` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1678 * Split S3 and S4 linters (and tests) into their own files by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1680 * sprintf_linter works for gettextf by @MichaelChirico in https://github.com/r-lib/lintr/pull/1679 * make Patrick required for tests by @MichaelChirico in https://github.com/r-lib/lintr/pull/1682 * `unnecessary_placeholder_linter()` covers a few other ops by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1681 * keep "configurable" tag accurate by @MichaelChirico in https://github.com/r-lib/lintr/pull/1683 * brace_linter() always allows {}/{ } by @MichaelChirico in https://github.com/r-lib/lintr/pull/1685 * Change lint changed settings; add note to tag tests by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1687 * Add examples to documentation: Part-8 (t-z) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1686 * Add examples to documentation: Part-9 (cleanup) by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1688 * Further reading for no tabs linter by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1690 * use parameterized test on public API for implicit_integer_linter() by @MichaelChirico in https://github.com/r-lib/lintr/pull/1693 * 1:10 doesnt lint, optionally by @MichaelChirico in https://github.com/r-lib/lintr/pull/1691 * Handle backtickd names in glue extraction by @MichaelChirico in https://github.com/r-lib/lintr/pull/1630 * use backports::import instead of custom approach by @MichaelChirico in https://github.com/r-lib/lintr/pull/1695 * don't lint c(...) in unneeded_concatenation_linter(!allow_single_expression) by @MichaelChirico in https://github.com/r-lib/lintr/pull/1699 * always close con by @MichaelChirico in https://github.com/r-lib/lintr/pull/1700 * Fix assignment_linter when comment further on in brace by @ashbaldry in https://github.com/r-lib/lintr/pull/1702 * unnecessary_lambda_linter catches functions with braces by @MichaelChirico in https://github.com/r-lib/lintr/pull/1704 * Style with styler and add directives where necessary to protect manual formatting by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1705 * Minor cleanup in tests by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1706 * Include `stringsAsFactors = FALSE` where relevant by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1708 * Get rid of `unnecessary_lambda_linter()` lint by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1710 * Wherever possible, prefer `expect_identical()` over `expect_equal()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1712 * Wherever possible, prefer `expect_identical()` over `expect_equal()`: Part-2 by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1713 * object_name_linter: add `regexes=` argument for custom style regexes by @AshesITR in https://github.com/r-lib/lintr/pull/1421 * add NEWS for #1421 by @AshesITR in https://github.com/r-lib/lintr/pull/1716 * Use public interface to test internal functions: Part-1 by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1720 * Improve object_usage_linter() by @AshesITR in https://github.com/r-lib/lintr/pull/1715 * Use public interface to test internal functions: Part-2 by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1721 * Move path linters to their own files by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1723 * customize lint message in literal_coercion_linter() by @MichaelChirico in https://github.com/r-lib/lintr/pull/1722 * Use public interface to test internal functions: Part-3 by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1726 * implement indentation_linter by @AshesITR in https://github.com/r-lib/lintr/pull/1411 * Print the code in `indentation_linter()` examples by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1732 * Remove warnings in examples for `expect_lint()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1734 * Sync up GHA workflows with `r-lib/actions` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1738 * Remove unnecessary `loadNamespace()` call for `{rex}` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1740 * Also check on windows R 4.1 by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1741 * Modify a few more examples by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1735 * Cleanup for a few tests by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1744 * Port over `pkgdown::in_pkgdown()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1747 * Run tests conditionally for `{mockery}` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1750 * Add "no suggests" R CMD check workflow by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1739 * Move utilities for package and project root to their own file by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1743 * `namespace_linter()` handles backticked operators by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1753 * Use `nolint` directives for `undesirable_operator_linter()` lints by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1748 * Further improvements for `lengths_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1749 * Add nolint for `ids_with_token()` definition by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1756 * Address `undesirable_function_linter()` lints by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1761 * use normalizePath() in find_package() by @AshesITR in https://github.com/r-lib/lintr/pull/1765 * Remove `undesirable_operator_linter()` from `.lintr_new` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1767 * Enhance and fix `indentation_linter` by @AshesITR in https://github.com/r-lib/lintr/pull/1758 * Add GHA workflow to detect link rot by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1772 * Improve pkgdown YAML formatting by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1773 * Fix examples and add a new workflow to catch any future failures by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1776 * Update URL for MegaLinter by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1779 * Use public interface to test internal functions: Part-4 by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1774 * Include only names of linters in example outputs by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1780 * Don't mark linter factory functions as linters in `modify_defaults()` by @AshesITR in https://github.com/r-lib/lintr/pull/1789 * Add sort_linter() by @Bisaloo in https://github.com/r-lib/lintr/pull/1528 * add PIPE to infix_metadata by @AshesITR in https://github.com/r-lib/lintr/pull/1793 * devtools -> remotes in installation instructions by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1796 * Add tests for `no_tab_linter()` with pipes by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1795 * Add test for native pipe in `commented_code_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1799 * Add tests for `spaces_inside_linter()` with pipes by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1798 * Implement `unnecessary_nested_if_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1783 * avoid <<- usage by @MichaelChirico in https://github.com/r-lib/lintr/pull/1809 * Tidy up DESCRIPTION file by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1802 * Update `.lintr_new` to include newly added linters by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1806 * Add CoC document by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1801 * safer Lint(), xml_nodes_to_lints() and lint() by @AshesITR in https://github.com/r-lib/lintr/pull/1788 * Fix missing row names in `available_linters()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1813 * Implement `implicit_assignment_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1814 * Remove `implicit_assignment_linter()` lints from tests by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1819 * Add `implicit_assignment_linter()` to config file by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1818 * add format methods for lint and lints by @AshesITR in https://github.com/r-lib/lintr/pull/1790 * Anticipate "no exceptions" case for `implicit_assignment_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1823 * Add tests for multi-line anonymous functions for `implicit_assignment_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1821 * Improve formatting for `brace_linter()` file by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1825 * Bump roxygen2 version by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1826 * More tests for `brace_linter()` with pipes and formula syntax by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1828 * Fix `sarif_output()` format when linting produces no results by @pdil in https://github.com/r-lib/lintr/pull/1837 * Fix R CMD check workflow by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1851 * rename with underscore for consistency by @MichaelChirico in https://github.com/r-lib/lintr/pull/1849 * `fixed_regex_linter()` doesn't fail with `"\\/"` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1846 * Allow `except = NULL` to remove all exceptions in `implicit_assignment_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1840 * Improve error message in `namespace_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1844 * consolidate find_* logic into one helper by @MichaelChirico in https://github.com/r-lib/lintr/pull/1852 * Reorganize tests for `unneeded_concatenation_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1859 * Consistently use Markdown lists with seealso tags by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1856 * Use braces in `tryCatch()` calls by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1839 * Improve deprecation messages for `find_*()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1861 * Deprecate `unnecessary_concatenation_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1862 * Start fresh session each time in RStudio by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1857 * Improve formatting for `fixed_regrex_linter()` tests by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1860 * friendlier error/warning for failing find_package() by @MichaelChirico in https://github.com/r-lib/lintr/pull/1850 * Add S3 group generics to `.base_s3_generics`, include exported S3 generics in generic list by @AshesITR in https://github.com/r-lib/lintr/pull/1842 * Implement `all_linters()` wrapper to access all available linters by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1854 * Add a list of accepted "misspelled" words by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1863 * Additional test for `unnecessary_placeholder_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1838 * Remove redundant nocov annotations in addins file by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1864 * Tests for multiple lints in `unnecessary_nested_if_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1834 * Add new map_vec() to purrr_mappers vector by @Bisaloo in https://github.com/r-lib/lintr/pull/1866 * Avoid implicit type coercion in conditional expressions using `length()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1865 * Tests for multiple lints in `implicit_assignment_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1833 * Ignore `{purrr}` on `R < 3.5` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1873 * Error on empty character vector args in undesirable linters by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1870 * Add document on how to get help by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1804 * Break down more tests into skip vs block pattern by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1876 * `implicit_assignment_linter()` doesn't produce false positives with walrus operator by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1878 * Separate out tests for allowed assignments with braces by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1872 * add assignment_as_infix = TRUE to indentation_linter() by @AshesITR in https://github.com/r-lib/lintr/pull/1812 * Fix `T_and_F_symbol_linter()` example by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1882 * Fix `condition_message_linter()` examples by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1881 * `missing_argument_linter()` allows missing arguments in `quote()` calls by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1890 * Revise exceptions to `implicit_assignment_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1817 * fix: update URLs to GHA workflow examples by @dpprdan in https://github.com/r-lib/lintr/pull/1901 * remove mentions of lintr-bot by @dpprdan in https://github.com/r-lib/lintr/pull/1902 * Add lifecycle badge to README by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1903 * update codecov badge link by @MichaelChirico in https://github.com/r-lib/lintr/pull/1916 * Bump R dependency to R3.5 by @MichaelChirico in https://github.com/r-lib/lintr/pull/1922 * Add examples for `unnecessary_lambda_linter()` by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1888 * Let MBCS warning fall through the first time it's encountered by @MichaelChirico in https://github.com/r-lib/lintr/pull/1923 * Use specific imports for {rex} by @MichaelChirico in https://github.com/r-lib/lintr/pull/1918 * Quit in-progress CI runs on new commit by @MichaelChirico in https://github.com/r-lib/lintr/pull/1925 * Use new expectations to check for absence of exceptions by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1880 * New `quotes_linter()` to replace `single_quotes_linter()` by @MichaelChirico in https://github.com/r-lib/lintr/pull/1931 * fix object_usage_linter with new xml2lang to avoid parsing comments by @MichaelChirico in https://github.com/r-lib/lintr/pull/1935 * Add `matrix_apply_linter()` by @Bisaloo in https://github.com/r-lib/lintr/pull/1869 * detect functional lambdas in object_usage_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1934 * Add missing spaces around "and" in glue_collapse() by @Bisaloo in https://github.com/r-lib/lintr/pull/1946 * Catch consecutive calls to assert_that in renamed consecutive_asserion_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1940 * Avoid implicit assignments in expectations by @IndrajeetPatil in https://github.com/r-lib/lintr/pull/1879 * fix nested tidy function call indentation by @AshesITR in https://github.com/r-lib/lintr/pull/1948 * ignore irrelevant symbols when pinpointing undefined variable lints by @MichaelChirico in https://github.com/r-lib/lintr/pull/1915 * Rename no_tab_linter to whitespace_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1955 * ns-qualify rex in tests by @MichaelChirico in https://github.com/r-lib/lintr/pull/1957 * Outdent code extracted from indented chunks by @MichaelChirico in https://github.com/r-lib/lintr/pull/1949 * prioritize tags over exclude_tags in available_linters by @MichaelChirico in https://github.com/r-lib/lintr/pull/1961 * handle multiline case for function_left_parentheses_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1960 * exclude deprecated linters from overall list & tag-specific lists by @MichaelChirico in https://github.com/r-lib/lintr/pull/1958 * Lint files in `exec` folder on lint_package by @jmaspons in https://github.com/r-lib/lintr/pull/1950 * Check for .lintr file in extra subdirectory by @tonyk7440 in https://github.com/r-lib/lintr/pull/1757 * Use XDG config directory by @klmr in https://github.com/r-lib/lintr/pull/460 * Minor typos in NEWS by @bahadzie in https://github.com/r-lib/lintr/pull/1973 * fix edge case in function_left_parentheses_linter by @MichaelChirico in https://github.com/r-lib/lintr/pull/1982 * tidying code after latest run by @MichaelChirico in https://github.com/r-lib/lintr/pull/1984 * fully deprecate positional arguments in lint()+friends by @MichaelChirico in https://github.com/r-lib/lintr/pull/1992 * More helpful failure message for object_usage_linter() by @MichaelChirico in https://github.com/r-lib/lintr/pull/1989 * more informative error message if a lintr config setting is broken by @AshesITR in https://github.com/r-lib/lintr/pull/1994 * Fix parsing usage warnings from codetools without location info by @AshesITR in https://github.com/r-lib/lintr/pull/1993 * Add an as_tibble method for class lints by @MichaelChirico in https://github.com/r-lib/lintr/pull/1998 * also a method for as.data.table.lints by @MichaelChirico in https://github.com/r-lib/lintr/pull/1999 * document "symbols" in object_name_linter (#1924) by @hedsnz in https://github.com/r-lib/lintr/pull/2001 ## New Contributors * @Bisaloo made their first contribution in https://github.com/r-lib/lintr/pull/1528 * @pdil made their first contribution in https://github.com/r-lib/lintr/pull/1837 * @jmaspons made their first contribution in https://github.com/r-lib/lintr/pull/1950 * @tonyk7440 made their first contribution in https://github.com/r-lib/lintr/pull/1757 * @bahadzie made their first contribution in https://github.com/r-lib/lintr/pull/1973 * @hedsnz made their first contribution in https://github.com/r-lib/lintr/pull/2001 **Full Changelog**: https://github.com/r-lib/lintr/compare/v3.0.2...v3.1.0