Giter VIP home page Giter VIP logo

lintr's Introduction

lintr

R build status codecov.io CRAN_Status_Badge lifecycle

{lintr} provides static code analysis for R. It checks for adherence to a given style, identifying syntax errors and possible semantic issues, then reports them to you so you can take action. Watch lintr in action in the following animation:

{lintr} is complementary to the {styler} package which automatically restyles code, eliminating some of the problems that {lintr} can detect.

Installation

Install the stable version from CRAN:

install.packages("lintr")

Or the development version from GitHub:

# install.packages("remotes")
remotes::install_github("r-lib/lintr")

Usage

And then you can create a configuration file and run selected linters:

lintr::use_lintr(type = "tidyverse")

# in a project:
lintr::lint_dir()

# in a package:
lintr::lint_package()

To see a list of linters included for each configuration:

# tidyverse (default)
names(lintr::linters_with_defaults())

# full
names(lintr::all_linters())

Setting up GitHub Actions

{usethis} provides helper functions to generate lint workflows for GitHub Actions:

# in a project:
usethis::use_github_action("lint-project")

# in a package:
usethis::use_github_action("lint")

You can also run lintr during continuous integration or within your IDE or text editor. See vignette("continuous-integration") and vignette("editors") for more details.

Without further configuration, this will run the default linters. See vignette("lintr") to learn how to modify these defaults.

Code of Conduct

Please note that the lintr project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.

lintr's People

Contributors

ashesitr avatar bisaloo avatar dependabot[bot] avatar dpprdan avatar dragosmg avatar eitsupi avatar f-ritter avatar fabian-s avatar fangly avatar gaborcsardi avatar huisman avatar indrajeetpatil avatar infotroph avatar jennybc avatar jhossepaul avatar jimhester avatar jonkeane avatar jrnold avatar kpagacz avatar krlmlr avatar laurentgatto avatar meo265 avatar michaelchirico avatar michaelquinn32 avatar renkun-ken avatar russhyde avatar salim-b avatar saurfang avatar schloerke avatar yu-iskw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lintr's Issues

`object_usage_linter` not working for S4 code?

Thanks for the great package, it's a nice help with grading programming assignments in addition to its general usefulness.

However, if I try to run the default linters on S4 code (here I just use some examples from the methods-package to make this reproducible) I get:

tmpfile <- tempfile(fileext = ".R")
writeLines(example(setClass, give.lines=TRUE), con=tmpfile)
lintr::lint(tmpfile)
## Error in typeof(fun) : object 'fun' not found
## Error in body(fun) : object 'fun' not found
writeLines(example(Methods, give.lines=TRUE), con=tmpfile)
lintr::lint(tmpfile)
## Error in typeof(fun) : object 'fun' not found
## Error in body(fun) : object 'fun' not found
traceback()
## 12: body(fun)
## 11: findLocalsList(c(list(body), dropMissings(formals)))
## 10: codetools::findFuncLocals(formals(fun), body(fun))
## 9: FUN(19L[[1L]], ...)
## 8: lapply(ids_with_token(source_file, rex(start, "FUNCTION"), fun = re_matches), 
##       function(loc) {
##           id <- source_file$parsed_content$id[loc]
##         [...]
## 7: linters[[linter]](expr)
## [...]
lintr::lint(tmpfile, 
     linters=default_linters[names(default_linters) != "object_usage_linter"])
## works as expected

sessionInfo():

R version 3.1.2 (2014-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] lintr_0.2.0

loaded via a namespace (and not attached):
 [1] codetools_0.2-8  crayon_1.1.0     digest_0.6.4     htmltools_0.2.6  igraph_0.7.1    
 [6] lazyeval_0.1.9   magrittr_1.5     packrat_0.4.1-1  parallel_3.1.2   rex_0.2.0       
[11] rmarkdown_0.3.3  stringdist_0.8.1 tools_3.1.2      yaml_2.1.13     

elpa availability

It is unclear if this package is available on elpa or any other Emacs package repository. It would make downloading and maintaining the latest version easier and more convenient.

'fun' not found

Transitioning from #25 to narrower scope:

> lint('EngineXLSX.R', with_defaults(line_length_linter(80)))
Error in body(fun) : object 'fun' not found

Source does execute without a hitch.

If you have a line with tabs it causes a bug in infix_spaces_linter

    if(a == b) {
    1
}
bad.R:1:1: style: Use two spaces to indent, never tabs.
    if(a == b) {
^
bad.R:1:11: style: Place a space before left parenthesis, except in a function call.
    if(a == b) {
          ^
bad.R:1:14: style: Put spaces around all infix operators.
    if(a == b) {
            ~^~~

The arrows are lined up properly on my machine, but there should not be any lint for infix operators in this case.

Strategy for invalidating the cache when package variables change.

The global variables lintr looks for variables defined in the current package, however if lintr is run before the function or variable is defined, then it is defined later the cache needs to be invalidated and re-linted so that the new variable is picked up.

Maybe the easiest thing to do is just never cache expressions that produce this specific lint...

vim integration issues

Syntastic is complaining:

syntastic: error: checker r/lintr returned abnormal status 1

Any thoughts? I'm guessing this is a path issue...

  • I've got lintr enabled in the vimrc per README:
...
" Syntastic
let g:syntastic_r_checkers = 1
let g:syntastic_enable_r_lintr_checker = 1
let g:syntastic_r_lintr_linters = "with_defaults(line_length_linter(80))"
  • I can load the lintr library in R
  • I've got the lintr.vim in the syntax checkers directory:
$ tree .vim/bundle/syntastic/syntax_checkers/r
.vim/bundle/syntastic/syntax_checkers/r
├── lint.vim
├── lintr.vim
└── svtools.vim
  • :echo executable('R') gives 1

Here is the debug output from syntastic:

"EngineXLSX.R" [dos] 659L, 26539C written                                                                                                                                                                                       
syntastic: 56.585804: &shell = '/bin/bash', &shellcmdflag = '-c', &shellpipe = '2>&1| tee', &shellquote = '', &shellredir = '>%s 2>&1', &shellslash = 0, &shelltemp = 1, &shellxquote = '', &shellxescape = ''                  
syntastic: 56.586569: UpdateErrors (auto): default checkers                                                                                                                                                                     
syntastic: 56.587531: CacheErrors: default checkers                                                                                                                                                                             
syntastic: 56.589386: g:syntastic_aggregate_errors = 0                                                                                                                                                                          
syntastic: 56.589759: getcwd() = /Users/tristan.wietsma/repos/ReportEngine                                                                                                                                                      
syntastic: 56.590313: CacheErrors: Invoking checker: r/lintr                                                                                                                                                                    
syntastic: 56.590954: SyntasticMake: called with options: {'errorformat': '%W%f:%l:%c: style: %m,%W%f:%l:%c: warning: %m,%E%f:%l:%c: error: %m,', 'makeprg': 'R --slave --restore --no-save -e ''library(lintr); lint(cache = TR
UE, commandArgs(TRUE), with_defaults(line_length_linter(80)))'' --args EngineXLSX.R', 'returns': [0]}                                                                                                                           
syntastic: 57.536184: checker output: ['Error in body(fun) : object ''fun'' not found', 'Calls: lint ... lapply -> FUN -> <Anonymous> -> findLocalsList -> body', 'Execution halted', '']                                       
syntastic: error: checker r/lintr returned abnormal status 1                                                                                                                                                                    
syntastic: 57.537370: r/lintr raw: []                                                                                                                                                                                           
syntastic: 57.537783: quiet_messages filter: {}                                                                                                                                                                                 
syntastic: 57.538355: aggregated: {'_sorted': 0, '_name': '', '_owner': 1, '_columns': 1, '_rawLoclist': []} 

Robustness linters

Suggestions from @hadley

  • Require drop = FALSE whenever [ is used with two or more args
  • Require na.rm argument for any function that has it (to force you to
    think about missing values during development)
  • Don't use of require()/library() inside a function
  • Don't change global state: setwd(), options(), par() etc. Could
    have next package robustr that contained alternatives (i.e. the with_*
    functions from devtools)
  • Always turn partial matching warning options on (not really code
    linting, but related)
  • Avoid functions that use NSE (with(), subset(), transform() etc)
  • Avoid functions that return different types of output (e.g. sapply())
  • TRUE and FALSE instead of T and F.
  • return() calls at the end of functions (result of last statement is always returned, so the explicit return() is redundant (and also slower as it requires an additional function call).
  • use of 1:length(x) in for loops (dangerous if length(x) can be 0) as 1:0 returns c(1, 0), should use seq_len(length(x)) instead.
  • Iterative appending to vectors within loops rather than pre-allocation the vectors and filling. (the former is quadratic in time complexity, as R has to keep re-sizing the vector on every assignment.
# bad
for (i in seq_len(10)) {
  x <- c(x, i)
}

# good
x <- integer(10)
for (i in seq_len(10)) {
  x[i] <- i
}

Choosing specific linters in SublimeText3

I just got SublimeLinter+lintr and R-Box working in ST3

I'm mainly interested in only a few selected linters - how do I modify the 'linters' settings in the following lines in the SublimeLinter user-settings file to use only, say, Syntax Error, object_usage_linter and absolute_paths_linter?

{
"user": {
"linters": {
"lintr": {
"@disable": false,
"args": [],
"cache": "TRUE",
"excludes": [],
"linters": "with_defaults(line_length_linter(120))"
}
}
}
}

Thanks for making a great package!

global variables not found?

screen shot 2014-12-29 at 12 17 19 am

Script global variables should be detected, but they are not.

SublimeLinter: r output:
/var/folders/w9/7w10svxj5093z4831n7m27w00000gp/T/SublimeLinter3-randy/untitled.lintr:8:5: warning: no visible global function definition for ‘foo’, Did you mean 'for'?
    foo(y) + increment
    ^~~
/var/folders/w9/7w10svxj5093z4831n7m27w00000gp/T/SublimeLinter3-randy/untitled.lintr:8:14: warning: no visible binding for global variable ‘increment’
    foo(y) + increment
             ^~~~~~~~~ 

Hadley Style linters

All from http://r-pkgs.had.co.nz/style.html Most should be pretty straight forward, some are a little tricky.

  • line length (80 120? + characters)
  • variable names
    • no camelCase
    • no multiple dots
    • very long variable names?
  • Spacing
    • infix operators have spaces around them
    • commas space after, none before
    • space before left parentheses
    • no spaces inside parentheses or square brackets
  • Curly braces
    • Open never on own line
    • Open always followed by a new line
    • Closing always on new line, unless an else
    • Code inside curly braces should be indented appropriately
  • no tab characters
  • Functions
    • verbs where possible
    • return() only for early returns (never at the end of a function)
  • Comments
    • should be a # followed by a space, or roxygen #'
  • only double quotes
  • <- not = for assignment

No discrimination against camelCase

Would you consider an option to precisely revert this one:

object_name_linter: check that objects
Are never camelCase
Are separated by _ rather than .
Are not more than width characters

Not everybody is a fan of snake_case, and a casual reading of R code demonstrated that no one style dominates. So maybe we should not impose one particular preference?

(I am less obsessed wit subpoints 2 and 3. I too dislike the dot as a word separator is it is confusing vis-a-vis the dispatch mechanism, but I am neutral on maxWidth. Or, as you would call it, max_width.

Cache by file contents as well as expressions.

The Rstudio Markers API #37 is easiest to use by just running devtools::lint(). However this is somewhat slow even when using caching because it has to lint every file in the package. If we cache the full file contents as well we can skip re-linting files which have not changed at all since the last time.

Lint r code from knitr documents

We should be able to lint R code from knitr documents. Just have to be careful to check if engine="r" or no engine specified.

linters = with_defaults is ignored in lint_package

I seem to succesfully exclude camel case when linting an individual file, but neither
lint_package(linters=with_defaults(object_camel_case_linter=NULL))

nor

lint_package(linters=with_defaults(camel_case_linter=NULL))
seem to have any effect (nor does setting this in .lintr)

Install fails on Windows, but not Linux

> library(devtools)
> install_github('jimhester/lintr')
Downloading github repo jimhester/lintr@master
Installing lintr
"C:/PROGRA~1/R/R-31~1.1/bin/x64/R" --vanilla CMD INSTALL  \
  "...\AppData\Local\Temp\Rtmp8qoe6m\devtools1d947a307bdc\jimhester-lintr-ec1622c"  \
  --library=".../R/win-library/3.1" --install-tests 

* installing *source* package 'lintr' ...
** R
** inst
** tests
** preparing package for lazy loading
Error : 'register_shortcuts' is not an exported object from 'namespace:rex'
Error : unable to load R code in package 'lintr'
ERROR: lazy loading failed for package 'lintr'
* removing '.../R/win-library/3.1/lintr'
Error: Command failed (1)

Windows session info:

> sessionInfo()
R version 3.1.1 (2014-07-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                           LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] devtools_1.6.1

loaded via a namespace (and not attached):
[1] httr_0.5       RCurl_1.95-4.3 stringr_0.6.2  tools_3.1.1 

Works fine on Linux:

> install_github('jimhester/lintr')
Downloading github repo jimhester/lintr@master
Installing lintr
'/opt/R-3.1.1/lib64/R/bin/R' --vanilla CMD INSTALL  \
  '/tmp/RtmpgFno2M/devtools2d475bc117cb/jimhester-lintr-ec1622c' --library='/opt/R3.1-lib' --install-tests 

* installing *source* package ‘lintr’ ...
** R
** inst
** tests
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
* DONE (lintr)

Linux session info:

> sessionInfo()
R version 3.1.1 (2014-07-10)
Platform: x86_64-unknown-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] devtools_1.6.1

loaded via a namespace (and not attached):
[1] httr_0.5       RCurl_1.95-4.3 stringr_0.6.2  tools_3.1.1  

I have no idea what's going on here. If you actually had a namespace issue, I'd think it would fail on both platforms.

Support RStudio

I don't know what linting they have in place already, but it would be nice to use this in RStudio as well.

Fast version of `adjust_columns`

The current implementation is very slow. Need to find a better implementation to fix this.

# using adjust_columns
system.time(get_source_expressions('~/test.R'))
#   user  system elapsed 
#  2.888   0.121   3.009 

# without adjust_columns
system.time(get_source_expressions('~/test.R'))
#   user  system elapsed 
#0.092   0.001   0.093 

Allow specifying default lint settings

There are a few options for this.

  • Global Settings
    • Just have a global config file somewhere
    • Contributing to packages with different settings may be annoying
  • Package specific settings
    • Put the config in the DESCRIPTION
    • Put them in the Rstudio project file
      • Allows use with any Rstudio project, so doesn't have to be a package
      • but you have to use Rstudio.
    • Use our own config file
      • Further pollutes the directory with a new file
      • Have to add it to .RBuildIgnore
      • However this might be easier to link to or contributers to find

Newline at end of file linter

while readLines does throw a warning in this case we should also generate a lint (and probably catch the warning).

This error is basically impossible to generate using vim and emacs, but very easy to do with RStudio.

Testing new linters

Should be as simple as possible to use and also have very informative error messages.

package names not be linted

if a name is in a library call it should be camelCase.

A workaround to this is just to quote the library name.

creating the GitHub comment fails if package has too many failing lints

I tried to enable lintr on travis in a package that has a lot of failing lints. It seems that the creation of the GitHub comment fails because it would be too long:

$ Rscript -e 'lintr::lint_package()'
Error in stop(httr::http_condition(response, "error", message = httr::content(response, :
{
"message": "Validation Failed",
"errors": [
{
"resource": "CommitComment",
"code": "custom",
"field": "body",
"message": "body is too long (maximum is 65535 characters)"
}
],
"documentation_url": "https://developer.github.com/v3/repos/comments/#create-a-commit-comment"
}
Calls: print -> print.lints -> github_comment
Execution halted

I guess just printing the first 50 to 100 lints would be sufficient in that case...

lint_package('lintr')

So since I haven't the foggiest idea what I'm doing (at the moment I'm trying to enforce camelCase instead of underscores_between_variable_name_bits, blah blah), I figured I'd start by linting lintr.

library(lintr)
lintrCheckResults <- lint_package('lintr')
## ...time passes...
## There were 50 or more warnings (use warnings() to see the first 50)
library(dplyr)
warnings() %>% unique
## Warning messages:
## 1: In source_file$parse : partial match of 'parse' to 'parsed_content'
lintrCheckResults %>% tail(5) %>% head(2)
## [[1]]
## ./tests/testthat/test-rstudio_markers.R:74:26: style: Variable and function names should be all lowercase.
##    expect_equal(marker3$basePath, "test"),
##                         ^~~~~~~~
##
## [[2]]
## ./tests/testthat/test-rstudio_markers.R:76:1: style: lines should not be more than 80 characters.
##    expect_equal(marker3$markers[[1]]$file, file.path("test", lint3[[1]]$filename)),
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## ...

So some thoughts (which will turn into a pull request momentarily):

  1. lintr could stand to pass its own linting (modulo bad.R, which should probably be in inst/example)
  2. it would be cool if results of linting a package could be organized in a hierarchy, like the package.

feature suggestion: cache

Running lint on large files (like editing an R script with a few hundreds/thousands of lines and saving often with a hook automatically calling lint) can result in pretty decent CPU load, which might be avoided by introducing some caching algorithm.

I could imagine at least two options to do so:

  • saving the results of lint of each R expression in a file named to the hash of the R expression
  • storing the results of lint of each file along with the last version of the file and run lint only on the parts highlighted in the diff

Probably the first option is easier to implement, e.g. that's what I'm doing with my evals function in pander relatively easily, although the user can choose if the cache should be stored on disk (permanent) or in an hidden R environment (fast but temporary).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.