lintr
is an R package offering static code analysis for R. It checks adherence to a given style, syntax errors and possible semantic issues, see the animation below. In this README find out
-
how to configure the project to e.g. tweak checks and ignore files;
-
how to setup
lintr
for on-the-fly checking in different editors; -
how to use
lintr
in combination with continuous integration;
If you need a bit automatic help for re-styling your code, have a look at the styler
package
Syntax errors
: reported by parse.Misencoded files
: check that files are read using the correct encoding.absolute_path_linter
: check that no absolute paths are used (e.g. "/var", "C:\System", "~/docs").assignment_linter
: check that<-
is always used for assignmentassignment_spaces_linter
: checks that assignments only have one space before and afterbackport_linter
: checks for usage of unavailable functions. Not reliable for testing r-devel dependencies.camel_case_linter
: check that objects are not in camelCase.closed_curly_linter
: check that closed curly braces should always be on their own line unless they are followed by an else.commas_linter
: check that all commas are followed by spaces, but do not have spaces before them.commented_code_linter
: check that there is no commented code outside of roxygen comments.cyclocomp_linter
: check for overly complicated expressions.duplicate_argument_linter
: check that no duplicate arguments are supplied to function calls.equals_na_linter
: check for x == NAextraction_operator_linter
: check that the[[
operator is used when extracting a single element from an object, not[
(subsetting) nor$
(interactive use).function_left_parentheses_linter
: check that all left parentheses in a function call do not have spaces before them.implicit_integer_linter
: check that integers are explicitly typed using the form1L
instead of1
.infix_spaces_linter
: check that all infix operators have spaces around them.line_length_linter
: check the line length of both comments and code is less than length.missing_argument_linter
: check that no missing argument is supplied to function calls.missing_package_linter
: check that no packages loaded bylibrary()
,require()
,loadNamespace()
, andrequireNamespace()
are missing.namespace_linter
: check if there are missing packages and symbols in namespace calls with::
and:::
.no_tab_linter
: check that only spaces are used, never tabs.nonportable_path_linter
: check that file.path() is used to construct safe and portable paths.object_length_linter
: check that function and variable names are not more thanlength
characters.object_name_linter
: check that object names conform to a single naming style, e.g. CamelCase, camelCase, snake_case, SNAKE_CASE, dotted.case, lowercase, or UPPERCASE.object_usage_linter
: check that closures have the proper usage using codetools::checkUsage(). Note this runs base::eval() on the code, so do not use with untrusted code.open_curly_linter
: check that opening curly braces are never on their own line and are always followed by a newline.paren_body_linter
: check that there is a space between right parenthesis and a body expression.paren_brace_linter
: check that there is a space between right parenthesis and an opening curly brace.pipe_call_linter
: force explicit calls in magrittr pipes.pipe_continuation_linter
: Check that each step in a pipeline is on a new line, or the entire pipe fits on one line.semicolon_terminator_linter
: check that no semicolons terminate statements.seq_linter
: check for1:length(...)
,1:nrow(...)
,1:ncol(...)
,1:NROW(...)
, and1:NCOL(...)
expressions. These often cause bugs when the right hand side is zero. It is safer to useseq_len()
orseq_along()
instead.single_quotes_linter
: check that only single quotes are used to delimit string constants.spaces_inside_linter
: check that parentheses and square brackets do not have spaces directly inside them.spaces_left_parentheses_linter
: check that all left parentheses have a space before them unless they are in a function call.sprintf_linter
: check that the numbers of arguments are correct and types of arguments are compatible insprintf("string", ...)
calls.todo_comment_linter
: check that the source contains no TODO comments (case-insensitive).trailing_blank_lines_linter
: check there are no trailing blank lines.trailing_whitespace_linter
: check there are no trailing whitespace characters.T_and_F_symbol_linter
: avoid the symbolsT
andF
(forTRUE
andFALSE
).undesirable_function_linter
: report the use of undesirable functions, e.g.options
orsapply
and suggest an alternative.undesirable_operator_linter
: report the use of undesirable operators, e.g.:::
or<<-
and suggest an alternative.unneeded_concatenation_linter
: check that thec
function is not used without arguments nor with a single constant.
Most of the default linters are based on Hadley Wickham's The tidyverse style guide.
There are several ways to use lintr
.
When inside an R session, lintr
can analyse all the R code in
a file (using lintr::lint(file_path)
), package (lintr::lint_package(pkg_path)
) or directory (lintr::lint_dir(dir_path)
).
Similarly, lintr
can be run from the command line using
Rscript -e "lintr::lint_package(commandArgs(trailingOnly = TRUE))" pkg_path
(linux/mac; for windows use Rscript.exe).
Advanced users may run lintr
during continuous-integration, or within their IDE or text editor.
Lintr supports per-project configuration of the following fields.
The config file (default file name: .lintr
) is in Debian Control Field Format.
linters
- see?with_defaults
for example of specifying only a few non-default linters.exclusions
- a list of filenames to exclude from linting. You can use a named item to exclude only certain lines from a file.exclude
- a regex pattern for lines to exclude from linting. Default is "# nolint"exclude_start
- a regex pattern to start exclusion range. Default is "# nolint start"exclude_end
- a regex pattern to end exclusion range. Default is "# nolint end"encoding
- the encoding used for source files. Default inferred from .Rproj or DESCRIPTION files, fallback to UTF-8
Below is an example .lintr file that uses:
- 120 character line lengths
- Excludes a couple of files
- Disables a specific linter, and;
- Sets different default exclude regexes
- Specifies the file encoding to be ISO-8859-1 (Latin 1)
linters: with_defaults(
line_length_linter(120),
commented_code_linter = NULL
)
exclusions: list("inst/doc/creating_linters.R" = 1, "inst/example/bad.R", "tests/testthat/exclusions-test")
exclude: "# Exclude Linting"
exclude_start: "# Begin Exclude Linting"
exclude_end: "# End Exclude Linting"
encoding: "ISO-8859-1"
With the following command, you can create a configuration file for lintr
that ignores all linters that show at least one error:
# Create configuration file for lintr
# Source this file in package root directory
# List here files to exclude from lint checking, as a character vector
excluded_files <- c(
list.files("data", recursive = TRUE, full.names = TRUE),
list.files("docs", recursive = TRUE, full.names = TRUE),
list.files("inst/doc", recursive = TRUE, full.names = TRUE),
list.files("man", recursive = TRUE, full.names = TRUE),
list.files("vignettes", recursive = TRUE, full.names = TRUE)
)
### Do not edit after this line ###
library(magrittr)
library(dplyr)
# Make sure we start fresh
if (file.exists(".lintr")) { file.remove(".lintr") }
# List current lints
lintr::lint_package() %>%
as.data.frame %>%
group_by(linter) %>%
tally(sort = TRUE) %$%
sprintf("linters: with_defaults(\n %s\n dummy_linter = NULL\n )\n",
paste0(linter, " = NULL, # ", n, collapse = "\n ")) %>%
cat(file = ".lintr")
sprintf("exclusions: list(\n %s\n )\n",
paste0('"', excluded_files, '"', collapse = ",\n ")) %>%
cat(file = ".lintr", append = TRUE)
# Clean up workspace
remove(excluded_files)
The resulting configuration will contain each currently failing linter and the corresponding number of hits as a comment. Proceed by successively enabling linters, starting with those with the least number of hits. Note that this requires lintr
0.3.0.9001 or later.
If you are developing a package, you can add ^\.lintr$
to your .Rbuildignore
file using usethis::use_build_ignore(".lintr")
.
You can configure lintr
to run as part of continuous integration (either for a package or a general project containing R files) in order to automatically check that commits and pull requests do not deteriorate code style.
If your package is on GitHub, the easiest way to do this is with GitHub Actions.
The workflow configuration files use YAML syntax. The usethis
package has some
great functionality, that can help you with workflow files. The most straightforward
way to add a lint
workflow to your package is to use the r-lib/actions's lint
example. To do this with usethis
, you need to call
usethis::use_github_action("lint")
This will create a workflow file called lint.yaml
and place it in the correct
location, namely in the .github/workflows
directory of your repository. This file configures all the steps required to run lintr::lint_package()
on your package.
lintr-bot will then add comments to the commit or
pull request with the lints found and they will also be printed as annotations along side the status check on GitHub. If you want to disable the commenting you can
set the environment variable LINTR_COMMENT_BOT=false
.
If you want to run lintr
on Travis-CI, you will need
to have Travis install the package first. This can be done by adding the
following line to your .travis.yml
r_github_packages:
- jimhester/lintr
We recommend running lintr::lint_package()
as an after_success step in your build process]
Just like with GitHub Actions, lintr-bot will then
add comments to the commit or pull request with the lints found and they will also be
printed on Travis-CI. If you want to disable the commenting you can
set the environment variable LINTR_COMMENT_BOT=false
.
after_success:
- R CMD INSTALL $PKG_TARBALL
- Rscript -e 'lintr::lint_package()'
Live example of a package using this setup: hibpwned
, lintr-bot commenting on a PR.
You are not limited you using lintr
for packages only, you can use it in combination with continuous integration for any other project.
If your project is on GitHub, you could take advantage of GitHub Actions and the usethis
functionality. r-lib/actions includes a lint-project
example, which you can use by calling:
usethis::use_github_action("lint-project")
To install the latest development version of lintr from GitHub
devtools::install_github("jimhester/lintr")
lintr lints are automatically displayed in the RStudio Markers pane, Rstudio versions (> v0.99.206).
Install lintr, type install.packages("lintr")
in the Console.
In order to show the "Markers" pane in RStudio: Menu "Tools" -> "Global Options...", a window with title "Options" will pop up. In that window: Click "Code" on the left; Click "Diagnostics" tab; check "Show diagnostics for R".
To lint a source file test.R
type in the Console lintr::lint("test.R")
and look at the result in the "Markers" pane.
This package also includes two addins for linting the current source and package. To bind the addin to a keyboard shortcut navigate to Tools > addins > Browse Addins > Keyboard Shortcuts. It's recommended to use Alt+Shift+L for linting the current source code and Ctrl+Shift+Alt+L to code the package. These are easy to remember as you are Alt+Shift+L(int) ;)
lintr has built-in integration with flycheck versions greater than 0.23
.
lintr is fully integrated into flycheck when using ESS. See the installation documentation for those packages for more information.
You can also configure what linters are used. e.g. using a different line length cutoff.
M-x customize-option
->flycheck-lintr-linters
->with_defaults(line_length_linter(120))
lintr can be integrated with syntastic for on the fly linting.
Put the file syntastic/lintr.vim
in syntastic/syntax_checkers/r
. If you are using
pathogen this directory is
~/.vim/bundles/syntastic/syntax_checkers/r
.
You will also need to add the following lines to your .vimrc
.
let g:syntastic_enable_r_lintr_checker = 1
let g:syntastic_r_checkers = ['lintr']
You can also configure what linters are used. e.g. using a different line length cutoff.
let g:syntastic_r_lintr_linters = "with_defaults(line_length_linter(120))"
lintr can be integrated with ALE for on the fly linting.
lintr is integrated with ALE and requires no additional installation.
You can configure what linters are used. e.g. using a different line length cutoff.
let g:ale_r_lintr_options = "with_defaults(line_length_linter(120))"
You can also configure whether lint
or lint_package
is used.
Set to 1 for lint_package
and 0 (default) for lint
.
let g:ale_r_lintr_lint_package = 1
See :h ale_r_lintr
for more information.
Note that configuration through .lintr
files are not supported.
lintr can be integrated with Sublime Linter for on the fly linting.
Simply install sublimeLinter-contrib-lintr
using Package Control.
For more information see Sublime Linter Docs
You can also configure what linters are used. e.g. disabling the assignment linter and using a different line length cutoff. In the SublimeLinter User Settings
{
"linters": {
"lintr": {
"linters": "with_defaults(assignment_linter = NULL, line_length_linter(120))"
}
}
}
lintr can be integrated with Linter for on the fly linting.
Simply install linter-lintr
from within Atom or on the command line with:
apm install linter-lintr
For more information and bug reports see Atom linter-lintr.
In Visual Studio Code, vscode-R presents the lintr diagnostics from languageserver.
Installing languageserver
package in R and vscode-R
extension in VS Code will enable lintr in VS Code by default or run the following command lines:
Rscript -e 'install.packages("languageserver")'
code --install-extension ikuyadeu.r