/flint

Find and Fix Lints in R Code

Primary LanguageROtherNOASSERTION

flint

R-CMD-check

flint is a small R package to find and replace lints in R code.

  • Lints detection with lint()
  • Automatic replacement of lints with fix()
  • Compatibility with (some) {lintr} rules
  • Fast
  • Low-dependency

flint is powered by astgrepr, which is itself built on the Rust crate ast-grep.

Installation

install.packages('flint', repos = c('https://etiennebacher.r-universe.dev'))

Usage

Optional setup:

  • setup_flint(): creates the folder flint and populates it with built-in rules as well as a cache file. You can modify those rules or add new ones if you want more control.

You can use flint as-is, without any setup. However, running setup_flint() enables the use of caching, meaning that the subsequent runs will be faster. It is also gives you a place where you can store custom rules for your project/package.

The everyday usage consists of two functions:

  • lint() looks for lints in R files;
  • fix() looks for lints in R files and automatically applies their replacement (if any).

One can also experiment with flint::lint_text() and flint::fix_text():

flint::lint_text("
any(is.na(x))
any(duplicated(y))
")
#> Original code: any(is.na(x)) 
#> Suggestion: anyNA(x) is better than any(is.na(x)). 
#> Rule ID: any_na-1 
#> 
#> Original code: any(duplicated(y)) 
#> Suggestion: anyDuplicated(x, ...) > 0 is better than any(duplicated(x), ...). 
#> Rule ID: any_duplicated-1
flint::fix_text("
any(is.na(x))
any(duplicated(y))
")
#> Old code:
#> any(is.na(x))
#> any(duplicated(y))
#> 
#> New code:
#> anyNA(x)
#> anyDuplicated(y) > 0

Comparison with existing tools

The most used tool for lints detection in R is lintr. However, lintr’s performance is not optimal when it is applied on medium to large packages. Also, lintr cannot perform automatic replacement of lints.

styler is a package to clean code by fixing indentation and other things, but doesn’t perform code replacement based on lints.

flint is extremely performant. This is a small benchmark on 3.5k lines of code with only three linters:

file <- system.file("bench/test.R", package = "flint")

bench::mark(
  lintr = lintr::lint(
    file, linters = list(lintr::any_duplicated_linter(), lintr::any_is_na_linter(),
                         lintr::matrix_apply_linter())
  ),
  flint = flint::lint(
    file, linters = list(flint::any_duplicated_linter(), flint::any_is_na_linter(),
                         flint::matrix_apply_linter()),
    verbose = FALSE
  ),
  check = FALSE
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 2 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 lintr         2.77s    2.77s     0.361   315.4MB     9.03
#> 2 flint      150.51ms 155.09ms     6.26     1.45MB     3.13

Contributing

Did you find some bugs or some errors in the documentation? Do you want flint to support more rules?

Take a look at the contributing guide for instructions on bug report and pull requests.

Acknowledgements

The website theme was heavily inspired by Matthew Kay’s ggblend package: https://mjskay.github.io/ggblend/.