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 likelint("text\n")orlint(text = "str"). You should setparse_settings=TRUEto 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=, andallow_pipe_assign=toassignment_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(), andunneeded_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 toids_with_token()andwith_id().- Passing linters by name or as non-
"linter"-classed functions. linter=argument ofLint().with_defaults().- Linters
closed_curly_linter(),open_curly_linter(),paren_brace_linter(), andsemicolon_terminator_linter().
- Argument
interpret_gluetoobject_usage_linter()is deprecated in favor of the more generalinterpret_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'smessageattribute 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 likeglue()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 ofall.equal()inifclauses or preceded by!(#2885, @Bisaloo). Such usages should wrapall.equal()withisTRUE(), for example.download_file_linter()encourages the use ofmode = "wb"(ormode = "ab") when usingdownload.file(), rather thanmode = "w"ormode = "a", as the latter can produce broken files in Windows (#2882, @Bisaloo).list2df_linter()encourages the use of thelist2DF()function, or thedata.frame()function when recycling is required, over the slower and less readabledo.call(cbind.data.frame, )alternative (#2834, @Bisaloo).coalesce_linter()encourages the use of the infix operatorx %||% y, which is equivalent toif (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 argumentfunction_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 ofseq_len(length(x))(#2577, @MichaelChirico). - recommends using
sequence()instead ofunlist(lapply(ints, seq))(#2618, @Bisaloo).
- recommends using
undesirable_operator_linter():- Lints operators in prefix form, e.g.
`%%`(x, 2)(#1910, @MichaelChirico). Disable this by settingcall_is_undesirable=FALSE. - Accepts unnamed entries, treating them as undesirable operators, e.g.
undesirable_operator_linter("%%")(#2536, @MichaelChirico).
- Lints operators in prefix form, e.g.
undesirable_function_linter()accepts unnamed entries, treating them as undesirable functions, e.g.undesirable_function_linter("sum")(#2536, @MichaelChirico).indentation_linter()handles un-bracedforloops correctly (#2564, @MichaelChirico).- Setting
exclusionssupports globs likeknitr*to exclude files/directories with a pattern (#1554, @MichaelChirico). object_name_linter()andobject_length_linter()apply to objects assigned withassign()or generics created withsetGeneric()(#1665, @MichaelChirico).object_usage_linter()gains argumentinterpret_extensionsto govern which false positive-prone common syntaxes should be checked for used objects (#1472, @MichaelChirico). Currently"glue"(renamed from earlier argumentinterpret_glue) and"rlang"are supported. The latter newly covers usage of the.envpronoun like.env$key, wherekeywas previously missed as being a used variable.boolean_arithmetic_linter()finds many more cases likesum(x | y) == 0where 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 byanyDuplicated(), e.g.n_distinct(col) == n()oruniqueN(col) == .N(#2482, @MichaelChirico).fixed_regex_linter()recognizes usage of the new (R 4.5.0)grepv()wrapper ofgrep();regex_subset_linter()also recommendsgrepv()alternatives (#2855, @MichaelChirico).object_usage_linter()lints missing packages that may cause false positives (#2872, @AshesITR)sprintf_linter()lintssprintf()andgettextf()calls when a constant string is passed tofmt(#2894, @Bisaloo).length_test_linter()is extended to check incorrect usage ofnrow(),ncol(),NROW(),NCOL()(#2933, @mcol).implicit_assignment_linter()gains argumentallow_paren_printto disable lints for the use of(for auto-printing (#2962, @TimTaylor).line_length_linter()has a new argumentignore_string_bodies(defaulting toFALSE) 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 consecutiveif()clauses (#2245, @MichaelChirico).
- Treats function bodies under the shorthand lambda (
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:1or1:-1(#2673, @MichaelChirico).missing_argument_linter()allows empty calls likefoo()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()andobject_length_linter()account for S3 class correctly when the generic is assigned with=(#2507, @MichaelChirico).assignment_linter()withoperator = "="does a better job of skipping implicit assignments, which are intended to be governed byimplicit_assignment_linter()(#2765, @MichaelChirico).implicit_assignment_linter()withallow_scoped=TRUEdoesn't lint forif (a <- 1) print(a)(#2913, @MichaelChirico).expect_true_false_linter()is pipe-aware, so that42 |> expect_identical(x, ignore_attr = TRUE)no longer lints (#1520, @MichaelChirico).T_and_F_symbol_linter()ignoresTandF:- 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).
- When used as symbols in formulas (
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 viaerrorCondition()orwarningCondition()not being compatible with thecall.argument instop()orwarning().
Other improvements
get_source_expression()captures warnings emitted by the R parser (currently always for mis-specified literal integers like1.1L) andlint()returns them as lints (#2065, @MichaelChirico).expect_lint()has a new argumentignore_order(defaultFALSE), which, ifTRUE, allows thechecks=to be provided in arbitary order vs. howlint()produces them (@MichaelChirico).- New
gitlab_output()function to output lints to GitLab format (#2858, @lschneiderbauer). - New argument
include_s4_slotsfor thexml_find_function_calls()entry in theget_source_expressions()to govern whether calls of the forms4Obj@fun()are included in the result (#2820, @MichaelChirico). use_lintr()adds the created.lintrfile to the.Rbuildignoreif 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 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_linterofallow_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_tokenis removed from settings. This was a vestige of the now-defunct support for posting GitHub comments.
New Contributors
- @lschneiderbauer made their first contribution in #2862
- @marberts made their first contribution in #2924
- @emmanuel-ferdman made their first contribution in #2938
- @mcol made their first contribution in #2942
- @TimTaylor made their first contribution in #2962
- @clerousset made their first contribution in #2961
Full Changelog: v3.2.0...v3.3.0-1