Skip to content

lintr 3.1.0

Compare
Choose a tag to compare
@jimhester jimhester released this 19 Jul 18:05

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: tidyverse/style#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 rstudio/tufte#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

New Contributors

Full Changelog: v3.0.2...v3.1.0