Skip to contents

lintr (development version)

Deprecations & breaking changes

Bug fixes

Changes to default linters

New and improved features

New linters

Lint accuracy fixes: removing false positives

Notes

  • All user-facing messages are now prepared using the cli package (#2418, @IndrajeetPatil). 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).

lintr 3.1.2

CRAN release: 2024-03-25

New and improved features

Lint accuracy fixes: removing false positives

Lint accuracy fixes: removing false negatives

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!!

lintr 3.1.1

CRAN release: 2023-11-07

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

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

Extensions to existing linters

Lint accuracy fixes: removing false positives

Lint accuracy fixes: removing false negatives

lintr 3.1.0

CRAN release: 2023-07-19

Deprecations & Breaking Changes

Bug fixes

Changes to defaults

New and improved features

New linters

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).

lintr 3.0.2

CRAN release: 2022-10-19

  • Fix test to avoid leaving behind cache files in the global cache directory.

lintr 3.0.1

CRAN release: 2022-09-13

  • Skip multi-byte tests in non UTF-8 locales (#1504)

  • modify_defaults() no longer uses the mistaken "lintr_function" S3 class, instead applying the "linter" class also common to Linter(). Linter() also includes "function" in the S3 class of its output to facilitate S3 dispatch to function methods where appropriate (#1392, @MichaelChirico).

Changes to defaults

New and improved features

Bug fixes

Other changes

lintr 3.0.0

CRAN release: 2022-06-13

Breaking changes

  • All linters are now function factories (i.e., functions that return functions) for consistency. Previously, only linters with customizable parameters were factories (#245, @fangly, @AshesITR, and @MichaelChirico).

    This means that usage such as lint("file.R", seq_linter) should be updated to lint("file.R", seq_linter()), and the following update for custom linters:

    my_custom_linter <- function(source_expression) { ... }
    
    # becomes
    my_custom_linter <- function() Linter(function(source_expression) { ... })
  • Exclusions specified in the .lintr file are now relative to the location of that file and support excluding entire directories (#158, #438, @AshesITR).

  • Removed long-deprecated linters (they’ve been marked as deprecated since v1.0.1 in 2017):

    • absolute_paths_linter()
    • camel_case_linter()
    • multiple_dots_linter()
    • snake_case_linter()
    • trailing_semicolons_linter()
  • Removed return() from all_undesirable_functions because early returns (which often improve readability and reduce code complexity) require explicit use of return(). Follow #1100 for an upcoming return_linter() to lint unnecessary return() statements (#1146, @AshesITR).

    Note that you can replicate old behavior by supplying return as a custom undesirable function: undesirable_function_linter(c(all_undesirable_functions, list(return = NA)))

Deprecations

  • Lints are now marked with the name of the linter that caused them instead of the name of their implementation function. Deprecated the obsolete linter argument of Lint() (#664, #673, #746, @AshesITR). Downstream custom linters should follow suit.
  • Renamed semicolon_terminator_linter() to semicolon_linter() for better consistency. semicolon_terminator_linter() survives but is marked for deprecation. The new linter also has a new signature, taking arguments allow_compound and allow_trailing to replace the old single argument semicolon, again for signature consistency with other linters.
  • The following linters were subsumed into brace_linter() and are now deprecated; see the item on brace_linter() below:
  • The ... argument for lint(), lint_dir(), and lint_package() has been promoted to an earlier position to better match the Tidyverse design principle of data->descriptor->details. This change enables passing objects to ... without needing to specify non-required arguments, e.g. lint_dir("/path/to/dir", linter()) now works without the need to specify relative_path. This affects some code that uses positional arguments (#935, @MichaelChirico).
    • For lint(), ... is now the 3rd argument, where earlier this was cache.
    • For lint_dir() and lint_package(), ... is now the 2nd argument, where earlier this was relative_path.
  • Deprecated argument source_file to exported functions with_id() and ids_with_token(). It has been renamed to source_expression to better reflect that this argument is typically the output of get_source_expressions(). For now, the old argument source_file can still be used (with warning). The now-private functional versions of many linters also underwent the same renaming (source_file -> source_expression). This has no direct effect on packages importing lintr, but is mentioned in case custom linters imitating lintr style had also adopted the source_file naming and want to adapt to keep in sync.
  • Deprecated with_defaults() in favor of linters_with_defaults(), and add modify_defaults() which is intended to be used more generally to modify (i.e., extend, trim, and/or update) a list of defaults. Note that the argument corresponding to with_defaults()’s default= is called defaults= (i.e., pluralized) in both of these, and that usage like with_defaults(default = NULL, ...) should be converted to linters_with_defaults(defaults = list(), ...) (#1029, #1336, #1361, @AshesITR and @michaelchirico).
  • Deprecated the find_line() and find_column() helpers from the item-level expressions returned with get_source_expressions(). These helpers were typically associated with regex-based logic for building linters, which is rarely needed and prone to false positives; now that lintr almost exclusively uses XPath-based logic for linters, these are no longer necessary (#1373, @MichaelChirico).

Other changes to defaults

Updates to default_linters

Other noteworthy changes

  • cyclocomp_linter(): set the default complexity_limit to 15. This brings the default into sync with what is enforced via default_linters (#693, @AshesITR).
  • lint_package() now lints files in the demo directory by default (#703, @dmurdoch).
  • Moved the default lintr cache directory from ~/.R/lintr_cache (which was a violation of CRAN policy) to R_user_dir("lintr", "cache"). Note that 3.0.0 is a major version update and invalidates the old cache anyway, so it can be safely deleted (#1062, @AshesITR).

New and improved features

New linters

Google linters

Google is a heavy user of lintr internally, and has developed a large set of linters improving code consistency and correcting common R usage mistakes. This release includes many of these linters that are of general interest to the broader R community. More will be included in future releases. See, e.g. #884, #979, #998, #1011, #1016, #1036, #1051, #1066, and #1067; special thanks to @MichaelChirico and @michaelquinn32.

Other features and improvements

  • Documentation: Reorganize linter documentation into new tag-based Rd pages (#888, #1015, @AshesITR).
    • Each linter has its own help page.
    • ?linters also links to tag help pages, collecting linters with a similar goal.
    • Each linter can have multiple tags.
    • available_linters(): new function to list available linters and their tags. This feature is extensible by package authors providing add-on linters for {lintr}.
    • available_tags(): new function to list available tags.
    • linters_with_tags(): new function to help build a list of linters using tags.
  • Encodings: lintr now supports non-system character Encodings. The correct encoding is auto-detected from .Rproj or DESCRIPTION files in your project. Override the default in the encoding setting of lintr (#752, #782, @AshesITR).
  • Jenkins CI: Support for writing comments to GitHub repo when running in Jenkins CI (#488, @fdlk).
  • Performance: Optimized performance-critical functions in lintr, such as get_source_expressions() resulting in about 2x speedup in our test suite and even more for complex files (#1169, #1197, #1200, #1201, #1214, @MichaelChirico and @AshesITR). Average lint_package() execution time is down about 30% and the median package sees about 40% improvement.
  • Raw strings: Several linters tightened internal logic to allow for raw strings like R"( a\string )" (#1034, #1285, @MichaelChirico and @AshesITR).
  • Selective exclusion syntax: New syntax to exclude only selected linters from certain lines or passages. Use # nolint: linter_name, linter2_name. or # nolint start: linter_name, linter2_name. in source files or named lists of line numbers in .lintr. Note the terminal . is required. Also allows for partial matching as long as the supplied prefix is unique, e.g. # nolint: infix_spaces. works to exclude infix_spaces_linter (#605, #872, @AshesITR).
    • Added the linter name to lintrs output to facilitate discovery of the correct name (#1357, @AshesITR).
  • Improved S3 generic detection for non-standard S3 generics where UseMethod() is called after several preceding expressions (#846, @jonkeane).
  • extraction_operator_linter(): no longer lint x[NULL] (#1273, @AshesITR).
  • is_lint_level(): new exported helper for readably explaining which type of expression is required for a custom linter. Some linters are written to require the full file’s parse tree (for example, single_quotes_linter()). Others only need single expressions, which is more cache-friendly (most linters are written this way to leverage caching) (#921, @MichaelChirico).
  • lint_dir() excludes the renv and packrat directories by default (#697, @AshesITR).
  • lint(): new optional argument text for supplying a line or lines directly, e.g. if the file is already in memory or linting is being done ad hoc (#503, @renkun-ken).
  • seq_linter(): improve lint message to be clearer about the reason for linting (#522, @MichaelChirico).
  • unneeded_concatenation_linter():
    • Correctly considers arguments in pipelines (%>% or |>; #573, #1270, @michaelquinn32 and @AshesITR).
    • New argument allow_single_expression, default TRUE, toggling whether c(x) should be linted, i.e., a call to c() with only one entry which is not a constant. In some such cases, c() can simply be dropped, e.g. c(a:b); in others, the parentheses are still needed, e.g. -c(a:b) should be -(a:b); and in still others, c() is used for the side-effect of stripping attributes, e.g. c(factor(letters)) or c(matrix(1:10, 5, 2)). In this last case, c() can (and should) in most cases be replaced by as.vector() or as.integer() for readability. In fact, we suspect it is always preferable to do so, and may change the default to allow_single_expression = FALSE in the future. Please report your use case if as.vector() does not suit your needs (#1344, @MichaelChirico).
  • use_lintr(): new exported helper for creating a minimal .lintr configuration (#902, @AshesITR).
  • xml_nodes_to_lints(): new exported helper for converting xml_node objects obtained using linter logic expressed in XPath into Lint objects (#1124, #1216, #1234, @MichaelChirico and @AshesITR).

Bug fixes

Internals

  • Added a new, more restrictive test workflow - test-package - that fails on warnings emitted by tests (#1263, #1272, @AshesITR).
  • Added a secondary, more restrictive lint workflow - lint-changed-files - for newly written / modified code (#641, @dragosmg).
  • Several optional Imported packages have become Suggested dependencies: httr, testthat, and rstudioapi. This should allow snappier CI builds for usages not relying on some more “peripheral” features of the package.
  • Special thanks to @bersbersbers for early testing on the 3.0.0 changes.
  • Switched CI from Travis to GitHub Actions, using the full tidyverse recommended R CMD check. Code coverage and linting are implemented using separate GitHub Actions workflows (#572, @dragosmg).
  • Updated R CMD GitHub Actions workflow to check for R 3.6 on Ubuntu, instead of R 3.3, and for R 4.0 on Windows, instead of R 3.6 (#803, @ dragosmg).
  • lintr now uses the 3rd edition of testthat (@MichaelChirico, @AshesITR, #910, #967).

lintr 2.0.1

CRAN release: 2020-02-19

New features

  • lintr now supports GitHub Actions and will print the lints as warning messages if lints are printed during an action.
  • lint_package() will now lint vignettes and data-raw by default (#447, @AshesITR).
  • lint_dir() will now include Rmd and Rnw files by default (@AshesITR).

Minor fixes and features

lintr 2.0.0

CRAN release: 2019-10-01

lintr 2.0.0 is a major release, and incorporates development changes since the last major release (1.0.0) in 2016-04-16.

Deprecated functions

  • Deprecated camel_case_linter(), snake_case_linter() and multiple_dots_linter() in favor of object_name_linter() which enforce the given style: snake_case, dotted.case, lowerCamelCalse, UpperCamelCase, alllowercase or ALLUPPERCASE (#59, @fangly).
  • Deprecated absolute_paths_linter() in favor of the new absolute_path_linter(), with a lax mode for fewer false positive lints (#199, @fangly).

New linters

New functions for writing linters

New functions for users

Linter fixes

General improvements and fixes

  • expect_lint() now no longer shows Rstudio markers and error messages are correctly preserved (#180, #211, @fangly)
  • Lint() / as.data.frame() error now fixed (#179, @fangly).
  • lint() no longer errors with inline \\Sexpr (#127).
  • lint() no longer errors with ‘<% %>’ constructs (#185).
  • lint_package() now works with the cache, as intended (#146, @schloerke)
  • lint_package() now excludes R/RcppExports.R by default (#282)
  • lint_package() now removes fully excluded files as soon as possible
  • lintr now looks up its configuration in any parent directories as well as the package directory (#238, #345)
  • seq_linter is now one of the default linters (#316).
  • Fix issue in lintr’s compatibility with R-devel, due to to a new version of the PCRE library (#411.)
  • read_settings() now has a better error message when the config file does not end with a newline (#160, #189)
  • expect_lint_free() is now automatically skipped when run on covr (#287)
  • Now lintr only tries to generate comments if running in wercker or travis CI (#166)
  • Add support for overriding GitHub API Token via GITHUB_TOKEN environment variable (#63, @mattyb)
  • Config files are now also searched for in the users’ home directory (#266, @randy3k)
  • Fixed crash caused by ambiguous cache file paths (#212, @fangly).
  • RStudio addins to lint current source and project (fixes #264, @JhossePaul)
  • Added proper handling of tab characters (fixes #44, @fangly)
  • lintr does not need the igraph package any more (#152, 1)
  • Fixed cache not saved in a directory other than requested (#213, @fangly) avoid reading and pre-processing of ignored files (@mwaldstein)
  • Allow for any number of # to start a comment. Useful in ESS (#299, @prosoitos)
  • R Markdown files that do not contain chunks are no longer treated as code (#370).
  • Fixed plain-code-block bug in Rmarkdown (#252, @russHyde)
  • Fixed bug where non-R chunks using {lang} engine format were parsed from R-markdown (#322, @russHyde)
  • Ensured lintr runs / installs / tests on R-3.6: pinned to github xmlparsedata; ensure vectors are length-1 when compared using && and || (#363 #377 #384 #391, @russHyde).

lintr 1.0.3

CRAN release: 2018-11-08

  • Fix tests to work with changes in the parser in R 3.6

lintr 1.0.2

CRAN release: 2017-11-08

  • Fix tests to work with upcoming testthat release.

lintr 1.0.1

CRAN release: 2017-08-10

  • bugfix to work with knitr 1.16.7
  • expect_lint_free() now is always skipped on CRAN. This is necessary because the non-binary R source may not be available when running tests on CRAN, and those tests may not be run in the package directory.

lintr 1.0.0

CRAN release: 2016-04-16

  • bugfix to work with testthat 1.0.0

lintr 0.3.3

CRAN release: 2015-09-15

  • infix_spaces_linter now properly checks = in named arguments. (#130, @saurfang).
  • commas_linter now properly recognizes lints when preceded by a blank line and points to the missing space rather than the comma (#111, #129, @saurfang).
  • Make spaces_left_parentheses_linter more robust when determining ( type (#128, @saurfang)
  • commented_code_linter (#83, @jackwasey)
  • Now trims long comments (#55, reported by @paulstaab)
  • Automatic commenting of Github commits and pull requests when linting on Travis-CI
  • expect_lint_free expectation can be added to testthat unit tests.
  • Robust configuration system and exclusion logic
  • Emacs and Sublime Text 3 plugins now available from their respective package repositories.
  • add names.lints, split.lints (#49, @ttriche)
  • Fixed bug that caused vim syntastic plugin not to work properly in windows (#46, @abossenbroek)
  • allow lintr customization per project using .lintr config files.
  • use globalenv() instead of baseenv() for default parent environment so that methods will be included.
  • do not check object usage if eval fails. Fixes (#24, reported by @fabian-s)
  • trailing_whitespace_linter was reporting the incorrect line number
  • Use RStudio source marker API to display lints (#37, @jjallaire)
  • Permit single quotes if they quote literal double quotes (#28, @jackwasey)
  • # nolint comments are respected with caching (#68, @krlmlr)
  • Properly handle all knitr document formats
  • Allow for (( when linting (#259, @nathaneastwood)
  • Remove ^ from infix spaces to conform with tidyverse. (#302, @nathaneastwood)

lintr 0.2.0

CRAN release: 2014-12-01

  • Initial release