You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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).
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).
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).