flir comes with an extensive set of rules taken from
lintr, but if necessary one can also extend it relatively
easily. This will require some knowledge of the Rust crate
ast-grep to write. This crate has great documentation on creating new
rules so you should start there.
In this vignette, our objective is to replace calls to
stop() by rlang::abort(), for instance because
we prefer the formatting of the output with the latter.
stop("this is an error")
#> Error: this is an error
rlang::abort("this is an error")
#> Error:
#> ! this is an errorFirst, we need to set up flir using
setup_flir(). This will create a flir folder
that contains (among other things) a rules folder where all
rules are stored. This is divided between builtin rules
(that shouldn’t modified manually) and custom rules (where
we will store our custom rule).
Then, we can create the structure of a new rule by copying an
existing rule, say flir/rules/builtin/any_is_na.yml for
instance. After removing the stuff that is specific to this rule, we end
up with this structure:
When we use stop(), we can pass multiple elements, like
in paste0():
This is not possible with rlang::abort(), which needs
everything to be in the argument message, meaning that we
need to manually put all those elements in paste0():
n <- 10
rlang::abort(paste0("Got ", n, " values instead of 1."))
#> Error:
#> ! Got 10 values instead of 1.Therefore, we can look for the pattern stop(...) and
replace it by rlang::abort(paste0(...)). Capturing all
elements in a pattern is done with $$$ and those elements
can be used in the fix or message arguments
using by wrapping them in ~~:
id: stop_abort-1
language: r
severity: warning
rule:
pattern: stop($$$ELEMS)
fix: rlang::abort(paste0(~~ELEMS~~))
message: Use `rlang::abort()` instead of `stop()`.After storing this rule in
flir/rules/custom/stop_abort.yml, we can call:
Running our example with lint_text() now shows the
message:
flir::lint_text(
'stop("Got ", n, " values instead of 1.")',
linters = "stop_abort"
)
#> Original code: stop("Got ", n, " values instead of 1.")
#> Suggestion: Use `rlang::abort()` instead of `stop()`.
#> Rule ID: stop_abort-1And fix_text() correctly applies the fix:
flir::fix_text(
'stop("Got ", n, " values instead of 1.")',
linters = "stop_abort"
)
#> Old code: stop("Got ", n, " values instead of 1.")
#> New code: rlang::abort(paste0("Got ", n, " values instead of 1."))Note that there are still some corner cases to address, such as
ignoring the arguments call. and domain of
stop() if they are specified, but this is not in the scope
of this vignette.
To automatically use this new linter without having to specify it
manually, we can add it to flir/config.yaml:
Running lint_text() or fix_text() without
linters now works:
The new linter is now set up, you can use flir as before
to lint or fix specific files or entire folders,
e.g. flir::fix_dir("R").