Diagnostic codes
Every finding ry reports carries a stable code. This page lists all of them.
How to read a finding
Section titled “How to read a finding”warning[unresolved]: I could not resolve `validte_url` in this package, its imports, or builtins. Did you mean `validate_url`? --> R/a.R:2:222 | check <- function(x) validte_url(x) ^^^^^^^^^^^| Part | Meaning |
|---|---|
warning | Severity — warning or error. --min-severity error reports only errors and exits on them alone |
[unresolved] | The diagnostic code. Stable across message rewordings: it is what you name in a suppression comment, in ry.toml, and what --output json reports as code |
R/a.R:2:22 | File, line, and character column of where the finding starts |
^^^^ | The exact source range the finding is about — the name, the token, or the expression, not the whole statement |
Some findings add a related location on a following line, as = note: — duplicate uses it to point at the other definition. See the CLI reference for the JSON shape.
Suppressing a finding
Section titled “Suppressing a finding”A # ry: allow(...) comment silences findings whose range starts on its own line (as a trailing comment) or on the line directly below it. There is no block or file scope.
| Comment | Silences |
|---|---|
# ry: allow(unused) | The one named code |
# ry: allow(unused, naming-style) | Every listed code |
# ry: allow(all) | Every code |
# ry: allow(unused)scratch <- 1L
total = 1L # ry: allow(assignment-operator)
flag <- T ## ry: allow(all)The marker is found by a line scan, not a parse: the first # on the line, all leading # stripped, then the literal ry: and allow( up to the first ). So ## ry: allow(x) and #ry:allow(x) both work — but a # inside a string literal earlier on the line moves where the scan starts.
Not suppressible, because these are not reported against an R source file: stub (reported on .Rtypes), config, and the unresolved / unused-import findings reported on NAMESPACE.
Silencing a whole class
Section titled “Silencing a whole class”| Directive | Effect | Scope |
|---|---|---|
# typing: off | Turns type checking off for this file, overriding [check] typing | File; must be a top-level comment |
# typing: on | Turns type checking on for this file | File |
# typing: strict or #: @strict | Turns on type checking and strict mode for this file | File |
# typing: off does not silence annotation findings — a malformed #: comment is reported whether or not the file is type-checked. An unrecognized value such as # typing: onn is itself reported as an annotation error.
Several R idioms are recognized directly, with no comment needed:
| Construct | Effect |
|---|---|
globalVariables(c("a", "b")) at top level | Those names never report unresolved, project-wide |
A file that calls R6Class | self, private and super resolve in it |
A name created by <<- anywhere in the file | Resolves |
A name starting with . or _ | Never reports unused |
library(pkg) for a package with no shipped stub | unresolved goes quiet, except for near misses of names your own project binds |
Formatting is suppressed by a different mechanism — # fmt: skip, # fmt: off / # fmt: on, # fmt: skip-file. See formatting rules.
The codes
Section titled “The codes”Most codes are on by default. These are the opt-ins, all configured in ry.toml:
| Code | Turn on with |
|---|---|
type-mismatch | [check] typing = true, or # typing: on in the file |
strict | [check] strict = true, or # typing: strict in the file |
naming-style | [lint] naming-style = "snake_case" or "camelCase" |
unused-parameter | [lint] unused-parameter = "warn" or "error" |
unused-import | [lint] unused-import = "warn" or "error" |
shadows-builtin | [lint] shadows-builtin = "warn" or "error" |
shadows-namespace | [lint] shadows-namespace = "warn" or "error" |
To go the other way: [check] unused = false, and any [lint] code set to "off".
Syntax
Section titled “Syntax”| Code | Severity | On by default | Triggered by |
|---|---|---|---|
syntax-error | error | yes | Anything the R parser rejects. The message varies with the failure (“unclosed (; expected ) to close the parameter list”); the code does not |
syntax-error | error | yes | A type expression inside a #: annotation that does not parse |
A statement that fails to parse as R suppresses every name-resolution and typing finding overlapping it — the checker draws no conclusions from source it could not read. A broken annotation does not suppress anything.
Name resolution
Section titled “Name resolution”| Code | Severity | On by default | Triggered by |
|---|---|---|---|
unresolved | warning | yes | A bare name read that resolves nowhere: not a local, not a top-level definition in this package, not a NAMESPACE import, not a builtin or stub export. Adds Did you mean for a near miss |
unresolved | warning | yes | pkg::name where the stub corpus knows pkg but pkg does not export name. pkg:::name is exempt — it legitimately reaches unexported names |
unresolved | warning | yes | pkg::name where pkg is neither a stub namespace nor a declared DESCRIPTION dependency: “unknown package namespace notapackage” |
unresolved | error | yes | In NAMESPACE: importFrom(pkg, name) where pkg has stubs and does not export name. An error rather than a warning because R refuses to load the package |
unresolved | error | yes (check only) | In NAMESPACE: export(name) naming something the package defines nowhere at top level — R CMD check’s “undefined exports”. Not reported by the language server |
unused | warning | yes | A write inside a function body that no read ever reaches, including a store overwritten before every read |
unused | warning | yes | In a script, a top-level binding nothing later reads. Package files are exempt — any file may use them. S3 method names are exempt: dispatch is not a read |
duplicate | warning | yes | A top-level name defined more than once across a package’s files. Both sites report, each with a note pointing at the other. Scripts are exempt — rebinding in a sequential script is ordinary |
Typing
Section titled “Typing”annotation covers malformed #: comments and is always on. type-mismatch and strict are opt-in; see type-system reference for the semantics behind them.
| Code | Severity | On by default | Triggered by |
|---|---|---|---|
annotation | error | yes | A type name that is not a builtin, a declared @type/@alias, or a stub class. Adds Did you mean |
annotation | error | yes | A malformed block: @forall after @param, @param after @return, more than one @return, a duplicate type-parameter name, @new with no nominal, or an unknown constraint (only numeric and atomic exist) |
annotation | error | yes | A dangling #: — no expression on the next line, a blank line in between, or no type expression at all |
annotation | error | yes | Type-argument arity: arguments applied to a non-generic, the wrong number of them, or a bare reference to a generic that needs them |
annotation | error | yes | A @type/@alias block that is not at file top level; @new naming an @alias rather than a @type; the same type name declared twice anywhere in the project (@type and @alias share one project-wide namespace) |
annotation | error | yes | An unrecognized # typing: directive value |
type-mismatch | error | no | An argument, or a returned value, whose type does not match the declared one |
type-mismatch | error | no | Calling something that is not a function, or a callee that may be NULL |
type-mismatch | error | no | Too many positional arguments, or a required argument missing |
type-mismatch | error | no | An argument name the function has no parameter for, or supplied twice. Names the parameter list and suggests the nearest match |
type-mismatch | error | no | No overload of an overloaded function matches the call |
type-mismatch | error | no | $ or [[ naming a field or position that does not exist (with a suggestion), or $ on an atomic vector |
type-mismatch | error | no | [[ on something that is not a list, an index that is not valid for the vector, or an index shape that is not modeled — x[], a named index, a multi-index (matrix and data-frame subsetting) |
type-mismatch | error | no | An operator applied to an operand, or a pair of operands, it is not defined for |
type-mismatch | error | no | for over something that is not a sequence |
type-mismatch | error | no | A constraint violated: numeric, atomic, or a scalar numeric |
type-mismatch | error | no | An alias cycle, or an infinite type (the occurs check) |
type-mismatch | error | no | Annotation rules: @if-unknown where the type is already known, an annotated parameter that also has a default, annotation parameters that do not match the function’s formals, or a []/[named] element type that is not atomic |
strict | error | no | An expression whose type is undetermined — “this expression has an undetermined type (Unknown)” |
strict | error | no | A reference with no known type — “could not determine the type of x” |
strict | error | no | A binding whose type does not stabilize across loop iterations |
strict | error | no | A binding defined recursively |
Enabling strict also raises every unresolved finding in the file from warning to error. The count does not change; the severity does, which matters if --min-severity error gates your build.
| Code | Severity | On by default | Triggered by |
|---|---|---|---|
assignment-operator | warning | yes | = used as assignment. The range is the = token alone |
boolean-shorthand | warning | yes | An identifier that is exactly T or F. Identifiers inside #: annotations are exempt, so a type variable named T is fine |
trailing-comma | error | yes | A comma after a call’s last argument. In R this supplies a missing argument rather than being ignored — hence error, unlike the other default-on lints |
naming-style | warning | no | An assignment target or a function parameter that does not match the configured casing. SCREAMING_SNAKE_CASE conforms under either style. Always a warning: the "warn"/"error" levels do not apply to this lint, which is configured by style value instead |
unused-parameter | as configured | no | A formal no read resolves to. ... is exempt, and S3 generics and their methods are exempt entirely — their formals are dictated by the generic |
unused-import | as configured | no | An importFrom(pkg, name) in NAMESPACE whose name appears in no token of any checked source. Whole-namespace import(pkg) is never checked. Reported by check only, not by the language server |
shadows-builtin | as configured | no | A top-level binding whose name base exports. Requires stubs to be installed |
shadows-namespace | as configured | no | A top-level binding whose name a non-base stub namespace declares and that resolves bare (stats::filter, utils::head) |
missing-comma is retired and never emitted — the parser rejects f(1 2) as a syntax error, exactly as R does. The config key still parses so old configs keep loading, and is ignored.
Tooling
Section titled “Tooling”| Code | Severity | On by default | Triggered by |
|---|---|---|---|
stub | error | yes | A declaration in a project’s stubs/*.Rtypes file that would otherwise be dropped in silence: a line that is not a name : TYPE declaration, an invalid name, a missing or invalid type, an unknown type name, or @masked on a non-variadic function type. The range covers the whole line |
config | error | yes | A malformed ry.toml — a TOML parse failure, or the wrong type of value on a known key. Reported on the config file by the language server only; check reports config failures as a usage error with exit code 2. An unknown key is not this finding: it is a check warning on stderr and never blocks loading |