Name | Modified | Size | Downloads / Week |
---|---|---|---|
Parent folder | |||
lintr 3.1.1 source code.tar.gz | 2023-11-09 | 3.5 MB | |
lintr 3.1.1 source code.zip | 2023-11-09 | 3.7 MB | |
README.md | 2023-11-09 | 12.4 kB | |
Totals: 3 Items | 7.2 MB | 0 |
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-suppliedexclude_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()
andfind_column()
entries fromget_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 argumentallow_pipe_assign
toTRUE
.object_usage_linter()
:- assumes
glue()
isglue::glue()
wheninterpret_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 tagsEQ_ASSIGN
,EQ_SUB
, andEQ_FORMALS
(#1977, @MichaelChirico).equals_na_linter()
checks forx %in% NA
, which is a more convoluted form ofis.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 theexclude_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
tolint_dir()
andlint_package()
(#972, @MichaelChirico). The default is still to show progress ininteractive()
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()
, andlint_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 bec(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 ofx$"a b"
).length_levels_linter()
for using the specific functionnlevels()
instead of checkinglength(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 encouragingif
statements to be structured asif (A) x else y
instead ofif (!A) y else x
(part of [#884], @MichaelChirico).repeat_linter()
for encouragingrepeat
for infinite loops instead ofwhile (TRUE)
(#2106, @MEO265).length_test_linter()
detects the common mistakelength(x == 0)
which is meant to belength(x) == 0
(#1991, @MichaelChirico).
Extensions to existing linters
fixed_regex_linter()
gains an optionallow_unescaped
(defaultFALSE
) to toggle linting regexes not requiring any escapes or character classes (#1689, @MichaelChirico). Thusfixed_regex_linter(allow_unescaped = TRUE)
would lint ongrepl("[$]", x)
but not ongrepl("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 likedplyr::filter(x, A & B)
in favor of usingdplyr::filter(x, A, B)
(part of [#884]; [#2110] and [#2078], @salim-b and @MichaelChirico). Optionallow_filter
toggles when this applies.allow_filter = "always"
drops such lints entirely, while"not_dplyr"
only lints calls explicitly qualified asdplyr::filter()
. The default,"never"
, assumes all unqualified calls tofilter()
aredplyr::filter()
.sort_linter()
checks for code likex == sort(x)
which is better served by using the functionis.unsorted()
(part of [#884], @MichaelChirico).paste_linter()
gains detection for file paths that are better constructed withfile.path()
, e.g.paste0(dir, "/", file)
would be better asfile.path(dir, file)
(part of [#884], [#2082], @MichaelChirico). What exactly gets linted here can be fine-tuned with theallow_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 withfile.path()
.seq_linter()
recommendsrev()
in the lint message for lints likenrow(x):1
(#1542, @MichaelChirico).function_argument_linter()
detects usage ofmissing()
for the linted argument (#1546, @MichaelChirico). The simplest fix forfunction_argument_linter()
lints is typically to set that argument toNULL
by default, in which case it's usually preferable to update function logic checkingmissing()
to checkis.null()
instead.commas_linter()
gains an optionallow_trailing
(defaultFALSE
) 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
, andrepeat
blocks, including combinations withbreak
andnext
statements. (#2105, @ME0265). implicit_assignment_linter()
gains an argumentallow_lazy
(defaultFALSE
) that allows optionally skipping lazy assignments likeA && (B <- foo(A))
(#2016, @MichaelChirico).unused_import_linter()
gains an argumentinterpret_glue
(defaultTRUE
) paralleling that inobject_usage_linter()
to toggle whetherglue::glue()
expressions should be inspected for exported object usage (#2042, @MichaelChirico).default_undesirable_functions
is updated to also includeSys.unsetenv()
andstructure()
(#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 aT_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 thatx %>% sprintf(fmt = "%s")
no longer lints (#1943, @MichaelChirico).condition_message_linter()
ignores usages of extracted calls likeenv$stop(paste(a, b))
(#1455, @MichaelChirico).inner_combine_linter()
no longer throws on length-1 calls toc()
likec(exp(2))
orc(log(3))
(#2017, @MichaelChirico). Such usage is discouraged byunnecessary_concatenation_linter()
, butinner_combine_linter()
per se does not apply.sort_linter()
only lints onorder()
of a single vector, excluding e.g.x[order(x, y)]
andx[order(y, x)]
(#2156, @MichaelChirico).redundant_ifelse_linter()
is aware ofdplyr::if_else()
'smissing=
argument, so thatif_else(A, TRUE, FALSE, missing = FALSE)
doesn't lint, butif_else(A, TRUE, FALSE, NA)
does (#1941, @MichaelChirico). Note thatdplyr::coalesce()
ortidyr::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 afterreturn()
orstop()
(#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]).