Directives
A directive is an ordinary # comment that tells arity to stand down. One
grammar covers the formatter, the linter, and both at once:
# arity[-format|-lint] <verb> [<rule>][: <reason>]
The verb says how far it reaches, the prefix says who it addresses:
| formatter | linter | both | |
|---|---|---|---|
| next | # arity-format skip: why | # arity-lint skip <rule>: why | # arity skip: why |
| from | # arity-format off | # arity-lint off <rule> | # arity off |
| to | # arity-format on | # arity-lint on | # arity on |
| file | # arity-format skip-file: why | # arity-lint skip-file <rule>: why | # arity skip-file: why |
Only a lint directive names a rule. # arity-format has no lint half to scope,
and # arity covers every rule by construction.
skip — the next statement
Applies to the next non-trivia sibling: the next piece of code after the comment, whatever that is. The attachment skips blank lines and other comments, so the directive can sit above a block of documentation and still land on the code below it.
# arity-lint skip unused-binding: part of the documented API
config <- list(width = 80)
A trailing comment attaches the same way — to the code that follows it, not the code on its own line:
x <- 1 # arity-lint skip browser: applies to the NEXT statement, not this one
browser()
For the formatter, skip hands the marked statement back byte for byte —
its own column, its interior alignment, its blank lines. Nothing about its
layout is decided, which is the whole point:
# arity-format skip: the rows are the matrix
m <- matrix(c(1, 0,
0, 1), nrow = 2)
off … on — a region
Everything between the two markers, or to the end of the file if the on never
comes. The markers themselves are ordinary comments and are formatted normally.
# arity-format off: generated by tools/codegen.R
lookup <- c(
"a" = 1,
"bb" = 22
)
# arity-format on
An on closes every region opened with the same prefix. # arity off and
# arity-format off are separate regions, and one does not close the other; an
on that closes nothing is reported by
misplaced-suppression.
skip-file — the whole file
For a generated or vendored file. # arity-format skip-file makes
arity format hand the file back unchanged, so --check reports it clean.
# arity-lint skip-file unused-binding: generated by tools/codegen.R
Every rule at once
Writing a : where the rule ID would go widens a lint directive to every
rule, including every rule arity ships in the future:
# arity-lint skip-file: generated, do not lint
The # arity column does the same by construction. Both are rarely what you
want at file or region scope; prefer the rule-scoped form, which
blanket-suppression will not flag.
In DESCRIPTION
The lint directives work in a DESCRIPTION too, on a line of their own:
Package: mypkg
# arity-lint skip unused-dependency: loaded reflectively by the plugin registry
Imports: somepkg
DCF has no trailing comments — a # in the middle of a value is part of that
value — so a directive must start at column zero, and one written after a field
applies to the field that follows it. A # line between the continuation
lines of a single field is the exception: R’s read.dcf skips it and resumes
the value, so a directive there covers the whole field it interrupts.
Of the formatter directives, only # arity-format skip-file is honored there.
Reasons
The text after the : is free-form and arity never interprets it — but telling
a tool to stand down is a standing claim that it is wrong here, and that claim
outlives whoever wrote it. Recording why keeps the next reader from having to
guess whether it was considered or expedient.
unexplained-suppression enforces the
convention; it is off by default, so enable it with select.
Directives are linted too
Directives fail silently by nature: when one goes wrong, the symptom is that
nothing happens, which is exactly what success looks like. Six meta rules make
those failures visible:
| Rule | Flags |
|---|---|
misnamed-suppression | a rule ID or a verb that does not exist |
misplaced-suppression | a directive that can never take effect |
blanket-suppression | a directive that names no rule |
unexplained-suppression | a directive with no reason (off by default) |
outdated-suppression | a directive that no longer silences anything |
deprecated-suppression | one of the spellings arity shipped with |
They read the directives of .R files only; a directive in a DESCRIPTION is
honored but not linted.
Limits
- One rule per directive. There is no comma-separated list:
# arity-lint skip a, breads the rule ID asa,and suppresses neither. Write a separate comment per rule.misnamed-suppressioncatches the mistake. - The formatter acts on whole statements. A
# arity-formatdirective is honored at the top level and in a block body — the places where whole lines can be handed back untouched. Between two call arguments it is inert;misplaced-suppressionsays so. A lint directive has no such limit: it attaches by node. - A format region does not leave its block. An unclosed
# arity-format offinside{ ... }ends at the closing brace. A lint region is a byte range and runs on untilonor end of file. - Syntax errors cannot be suppressed. A file that does not parse is reported
before any rule runs, so
# arity-lint skip syntax-errorhas no effect (and is itself flagged as a misnamed rule). metafindings need the file-wide form. A finding about a directive is spanned on a comment, and askipattaches past comments to the next piece of code — so it can never land on the directive above it. Use# arity-lint skip-file <meta-rule>: <reason>, or turn the rule off in configuration with[lint] ignore.- A bare
# arityneeds a verb.# arity is greatis prose, not a typo’d directive, so it is left alone rather than reported. The prefixed forms are unambiguous and are checked. - Roxygen lines are not directives.
#' arity-lint …is documentation content, not a directive.
Deprecated spellings
# arity-ignore <rule>: <reason> and # arity-ignore-file <rule>: <reason> are
what the linter shipped with. They still work, and mean exactly
# arity-lint skip and # arity-lint skip-file — but they are deprecated, and
deprecated-suppression flags them with a
safe autofix, so arity lint --fix migrates a codebase in one pass:
arity lint --fix --select deprecated-suppression .
Mixing the two (# arity-ignore skip <rule>) is an error, and
misnamed-suppression reports it.
Turning a rule off entirely
For a rule you never want, arity.toml is the better tool than a comment in
every file:
[lint]
ignore = ["unused-binding"]
See Configuration.