Learn R Programming

flir

(pronounced “fleer” as in “beer”)

flir is an R package to detect and rewrite code patterns. It was originally created to be an R linter and an alternative to lintr. However, it may be better to view it as a tool to refactor any type of code by detecting and rewriting custom patterns (see Adding new rules). flir comes with a list of built-in rules and therefore can still be used as a linter (see the “Usage” section below), but I now concentrate my efforts on a new R linter entirely written in Rust: Jarl. Therefore, I will not add new rules in flir.

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

Installation

To get the CRAN version (stable):

install.packages("flir")

To get the development version (unstable):

# install.packages("remotes")
remotes::install_github("etiennebacher/flir")

Usage

Optional setup:

  • setup_flir(): creates the folder flir 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 flir as-is, without any setup. However, running setup_flir() 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 rule violations in R files;
  • fix() looks for rule violations in R files and automatically applies their replacement (if any).

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

flir::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
flir::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

See the vignette Automatic fixes to see how to be more confident about changes introduced by flir.

Real-life examples

I tested flir on several packages while developing it. I proposed some pull requests for those packages. Here are a few:

Except for some manual tweaks when the replacement was wrong (I was testing flir after all), all changes were generated by flir::fix_package() or flir::fix_dir(<dirname>).

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.

flir is quite fast This is a small benchmark on 3.5k lines of code with a few linters:

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

bench::mark(
  lintr = lintr::lint(
    file,
    linters = list(
      lintr::any_duplicated_linter(),
      lintr::any_is_na_linter(),
      lintr::matrix_apply_linter(),
      lintr::function_return_linter(),
      lintr::lengths_linter(),
      lintr::T_and_F_symbol_linter(),
      lintr::undesirable_function_linter(),
      lintr::expect_length_linter()
    )
  ),
  flir = flir::lint(
    file,
    linters = list(
      flir::any_duplicated_linter(),
      flir::any_is_na_linter(),
      flir::matrix_apply_linter(),
      flir::function_return_linter(),
      flir::lengths_linter(),
      flir::T_and_F_symbol_linter(),
      flir::undesirable_function_linter(),
      flir::expect_length_linter()
    ),
    verbose = FALSE,
    open = 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         3.44s    3.44s     0.291   313.5MB    12.2 
#> 2 flir       153.77ms 172.76ms     5.89      1.8MB     1.96

Why the name “flir”?

flir was originally named flint but I had to rename it to avoid conflicts with a package named flint on CRAN.

flir stands for “Fix Lints In R”.

Contributing

Did you find some bugs or some errors in the documentation? Do you want flir 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/.

Copy Link

Version

Install

install.packages('flir')

Monthly Downloads

276

Version

0.6.0

License

MIT + file LICENSE

Issues

Pull Requests

Stars

Forks

Maintainer

Etienne Bacher

Last Published

December 14th, 2025

Functions in flir (0.6.0)

missing_argument_linter

Missing argument linter
redundant_equals_linter

Block usage of ==, != on logical vectors
outer_negation_linter

Require usage of !any(x) over all(!x), !all(x) over any(!x)
list_comparison_linter

Block usage of comparison operators with known-list() functions like lapply
lint

List all lints in a file or a directory
expect_null_linter

Require usage of expect_null for checking NULL
expect_s3_class_linter

Require usage of expect_s3_class()
nzchar_linter

Require usage of nzchar where appropriate
todo_comment_linter

TODO comment linter
sample_int_linter

Require usage of sample.int(n, m, ...) over sample(1:n, m, ...)
undesirable_function_linter

Undesirable function linter
length_levels_linter

Require usage of nlevels over length(levels(.))
seq_linter

Sequence linter
right_assignment_linter

right_assignment
library_call_linter

Library call linter
export_new_rule

Create a custom rule for external use
lengths_linter

Require usage of lengths() where possible
rep_len_linter

Require usage of rep_len(x, n) over rep(x, length.out = n)
expect_comparison_linter

Require usage of expect_gt(x, y) over expect_true(x > y) (and similar)
equals_na_linter

Equality check with NA linter
expect_type_linter

Require usage of expect_type(x, type) over expect_equal(typeof(x), type)
length_test_linter

Check for a common mistake where length is applied in the wrong place
package_hooks_linter

Package hooks linter
vector_logic_linter

Enforce usage of scalar logical operators in conditional statements
nested_ifelse_linter

Block usage of nested ifelse() calls
paste_linter

Raise lints for several common poor usages of paste()
numeric_leading_zero_linter

Require usage of a leading zero in all fractional numerics
which_grepl_linter

Require usage of grep over which(grepl(.))
expect_identical_linter

Require usage of expect_identical(x, y) where appropriate
expect_length_linter

Require usage of expect_length(x, n) over expect_equal(length(x), n)
redundant_ifelse_linter

Prevent ifelse() from being used to produce TRUE/FALSE or 1/0
sort_linter

Check for common mistakes around sorting vectors
stopifnot_all_linter

Block usage of all() within stopifnot()
setup_flir_gha

Create a Github Actions workflow for flir
setup_flir

Setup flir
for_loop_index_linter

Block usage of for loops directly overwriting the indexing variable
function_return_linter

Lint common mistakes/style issues cropping up from return statements
undesirable_operator_linter

Undesirable operator linter
unnecessary_nesting_linter

Block instances of unnecessary nesting
literal_coercion_linter

Require usage of correctly-typed literals over literal coercions
list_linters

Get the list of linters in flir
any_duplicated_linter

Require usage of anyDuplicated(x) > 0 over any(duplicated(x))
T_and_F_symbol_linter

T and F symbol linter
double_assignment_linter

double_assignment
class_equals_linter

Block comparison of class with ==
any_is_na_linter

Require usage of anyNA(x) over any(is.na(x))
duplicate_argument_linter

Duplicate argument linter
add_new_rule

Create a custom rule for internal use
equal_assignment_linter

equal_assignment
empty_assignment_linter

empty_assignment
condition_message_linter

Block usage of paste() and paste0() with messaging functions using ...
expect_s4_class_linter

Require usage of expect_s4_class(x, k) over expect_true(is(x, k))
expect_true_false_linter

Require usage of expect_true(x) over expect_equal(x, TRUE)
is_numeric_linter

Redirect is.numeric(x) || is.integer(x) to just use is.numeric(x)
implicit_assignment_linter

implicit_assignment
expect_not_linter

Require usage of expect_false(x) over expect_true(!x)
expect_named_linter

Require usage of expect_named(x, n) over expect_equal(names(x), n)
flir-package

flir: Find and Fix Lints in R Code
matrix_apply_linter

Require usage of colSums(x) or rowSums(x) over apply(x, ., sum)
fix

Automatically replace lints