Get your codebase lint-free forever with lintr

[This article was first published on Maëlle's R blog on Maëlle Salmon's personal website, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Writing good code is hard. Some aspects get easier with experience – although I observe that I consistently forget some things. 🙈 Other aspects can be tackled through code review – although your reviewer’s time will be better spent on design questions than on nitpicks. 💅 Static code analysis can help with code quality.

In this post I will show how I set up packages to benefit from the lintr R package for static code analysis.

Step 1: Add a lintr configuration file

I start by creating (and .Rbuildignoring) a .lintr file at the root of my package with contents indicating I want to run all available linters (there are so many wonderful linters!).

linters: lintr::linters_with_tags(tags = NULL)
encoding: "UTF-8"

Now the hard work can start…

Step 2: Run lintr, fix or skip or configure until done

I then run lintr::lint_package() and have a look at the output. It can be quite humbling. 😅

For each marker, I can…

Fix the problem

For instance if a line is too long, I might split it into two, going from

config_lines <- brio::read_lines(<super-long-path>)

to

config_path <- <super-long-path>
config_lines <- brio::read_lines(config_path)

I can also change function calls from long to wide, create helper functions, etc.

I might notice some linters come up especially often which makes me vow to try and remember about them, for instance writing explicit integers (2L) rathen than implicit ones (2).

Skip a linter for a line

For some marked lines that I view as non problematic or as a false positive, I will add a nolint: <name-of-the-linter> exclusion to the specific line.

Configure lintr

I might also decide to stop using some linters, like the complicated indentation one, or to exclude whole files, like tests/testthat.R.

For instance in a package the .lintr might end up looking like this:

linters: lintr::linters_with_tags(tags = NULL, indentation_linter = NULL)
encoding: "UTF-8"
exclusions: list(
    # excluded from all lints:
    "tests/testthat.R"
    )

Repeat until a clean output

After a few (or more, let’s be honest) cycles of running lintr and responding to part of the output, lintr has nothing left to complain about! The codebase is in a good shape. 🎉

Step 3: Set up lintr on continuous integration

At this point I call usethis::use_github_action("lint-changed-files") that will download a GitHub Actions workflow for me. This workflow tells GitHub Actions to run lintr in pull requests, only for files changed in the pull request.

Having this safeguard means that I’ll never have to run step 2 ever again, as problems in new code will be flagged (and hopefully addressed) continuously. I might only have to run step 2 again because of newly released linters.

An alternative approach might be to use pre-commit to run lintr before making any commit.

Conclusion

In this post I explained how I set up lintr safeguards in packages where I decide to get serious about code quality:

  • first creating a basic lintr configuration file,
  • then running lintr, editing my code and lintr configuration over and over again until lintr marks nothing,
  • adding a continuous integration workflow that will run lintr on files changed by every pull request.

Thanks to lintr maintainer Michael Chirico and to all other lintr authors and contributors. 🙏

How do you use lintr? Have you heard of flint by Etienne Bacher, that does not only flags problems but also fixes them, for a subset of lintr’s linters?

To leave a comment for the author, please follow the link and comment on their blog: Maëlle's R blog on Maëlle Salmon's personal website.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)