Limitations
When ry cannot determine a type, the value becomes Unknown, which is compatible with everything, so
most gaps on this page are checks that are silently skipped, and a clean run proves less than it
seems. Strict mode reports where that happens. The
last section lists the places where ry reports correct code instead.
Data frames
Section titled “Data frames”Columns have no types, so everything read out of a data frame is Unknown:
#: fn(df: data.frame) -> integercount_rows <- function(df) df$whateverThis passes, although the column may not exist. It is the gap that matters most for analysis code,
because analysis code lives in data frames: df: data.frame checks that a data frame arrives, not
which columns it has. data.frame() itself returns Unknown, so it is usually the first place
strict mode reports in data code.
Matrices have the same gap one level down: matrix arithmetic is checked, but dimensions are not, so a non-conformable product goes unreported.
Object systems
Section titled “Object systems”S4 and R6 objects are Unknown, and calls to an S3 generic that dispatches through UseMethod()
return Any, because their behavior is decided at run time from class attributes. Strict mode
reports S4 objects, but not R6 objects or S3 calls. Operator methods such as +.Date and methods
called directly are checked, and structure(x, class = "dog") keeps x’s type, because a class
attribute is data. The tour shows how to
give your own classes checked types.
Packages
Section titled “Packages”Names from a package with no stub are Unknown, and attaching such a package
switches off unresolved-name reports for the whole project.
A project’s own %op% operators are left untyped, because one may quote its right operand instead of
evaluating it, as magrittr’s %>% does, and checking it as a call would reject correct code.
What is analyzed together
Section titled “What is analyzed together”source() is not followed, so a function from a sourced file is reported as unresolved where you
call it, and as unused where you define it. Moving shared helpers into R/, with a ry.toml at the
project root, makes them visible to every script. Files under R/ share one namespace, and every other file is analyzed
on its own, seeing its own definitions plus the package’s. Files directly under tests/testthat/ are
analyzed as one environment with the helpers first, which is more permissive than testthat: a name
one test file defines and another uses resolves in ry and fails when the tests run.
ry check reads the R chunks of .Rmd, .qmd, and .Rnw documents, but the language server does
not analyze them and ry fmt skips them.
Where correct code is reported
Section titled “Where correct code is reported”These are known bugs, not design decisions:
- Mixed numbers into an unannotated function.
f <- function(price, rate) price * rateinfers one type for both parameters, sof(1L, 0.5)is reported. Separately, an unannotated parameter used in arithmetic rejects alogical, sog <- function(x) x * 2; g(TRUE)is reported. Annotating the parameters asdoublefixes both. - A vector meeting an unannotated parameter. In
g <- function(x) x + c(1L, 2L),xis assumed to have length one, sog(c(1, 2))is reported. Annotatexasdouble[]. - A list filled by position.
res <- list(); res[[i]] <- xin a loop infers a dict-like list, and a laterres[[1]]is reported. Annotate the empty list as#: list[T]. - The loop variable after a loop. At the top level of a file, reading
iafterfor (i in xs) {}is reported as unresolved, although R keeps its last value. - A variable named like a function. After
mean <- 3, the callmean(x)is reported as calling a number, although R skips non-function bindings when it looks up a function. - Column names in tidyselect and masking functions of untyped packages. Only packages with typed
declarations (dplyr, data.table, ggplot2, testthat, and base R) mark which arguments are column
names, so after
library(tidyr),pivot_longer(d, cols = c(a, b))reportsaandb. A top-levelutils::globalVariables(c("a", "b"))silences them, as it does forR CMD check. - A trailing comma for rlang’s dynamic dots. Functions built on rlang, such as dplyr’s verbs,
accept
mutate(d, y = 1, ), but ry reports the trailing comma as an error, as it would forc(1, 2, ). - Package-qualified calls outside a package.
pkg::fun()reports an unknown namespace unless some file in the project callslibrary(pkg), and for a package ry has no stub or export list for, even then. In a package, the same report for a package missing fromDESCRIPTIONis correct, because R CMD check warns about it too. - Strict mode.
if (x < 0) stop("negative")reports thestop()call as an undetermined type.# ry: allow(strict)on the line silences it.