Skip to content

0.4.0 - 2026-07-13

Choose a tag to compare

@github-actions github-actions released this 13 Jul 19:58

Release Notes

Precision release driven by the top-300 CRAN audit: the corpus total fell
from ~23,300 diagnostics to ~6,500 (-72%) while every confirmed real bug
in the audit's regression list still surfaces, and the new rule family
found previously unknown bugs (scales !length(x) == 1 guards among
them).

Added

  • Typed and required parameter metadata in typeshed signatures, including
    numeric mode unions and strict validation through ry typeshed validate.

  • R-compatible exact, partial, and positional call-argument matching with
    RY090 for unknown named arguments, RY091 for missing required arguments,
    and RY092 for provable argument type mismatches.

  • Runtime custom typeshed loading through the typeshed key in ry.toml and
    repeatable --typeshed flags. Flat and nested stub layouts are supported,
    later directories replace earlier packages, and editor diagnostics use the
    same workspace configuration.

  • The embedded typeshed is now a vendored snapshot of the standalone
    r-typeshed repository, with schema-version validation and source metadata.

  • New mis-parenthesization rule family: RY093 (comparison inside
    length()/nchar()/abs(), also detected inside &&/|| operands),
    RY095 (!x == y negation-comparison precedence), and RY096
    (hasArg() naming a non-formal of the enclosing function).

  • RY094: printf-family (sprintf/gettextf) literal format strings are
    checked against the supplied argument count.

  • RY097: files whose top-level statements are mostly unparseable (Ratfor
    sources, broken fixtures) collapse into a single info diagnostic instead
    of hundreds of spurious errors.

  • RY098: a parameter default referencing a body-local is flagged when an
    execution path can force the default before the local is assigned;
    the idiomatic late-bound default stays silent.

  • Confidence tiers: every diagnostic carries high/medium/low
    confidence, output is ranked by tier, diagnostics under tests/,
    data-raw/, demo/, vignettes/, and inst/ are demoted one tier, and
    --min-confidence filters both output and exit code. A symbol used in
    value position that only resolves to a function from another namespace is
    reported at high confidence with the resolution target in the message.

  • Baseline workflow for incremental adoption: ry check --write-baseline
    snapshots current diagnostics (line-number-free matching) and
    --baseline / the baseline config key subtracts them from later runs.

  • Package-aware scan contexts: tests/testthat/ files see the package's
    own namespace, testthat, DESCRIPTION Depends/Suggests, and
    helper-*.R/setup-*.R bindings; data-raw/, demo/, and vignettes/
    attach Depends; .Rbuildignore patterns (Perl regexes) are respected
    without ever excluding R/ or tests/.

  • NSE completion: rlang {{ }} embrace is recognized as a mask escape
    (typos inside it still flagged), and the .data$col / .data[["col"]] /
    .env$var pronouns resolve against the mask schema or lexical scope.

  • Minimum-viable S4 modeling: in-package setClass/setGeneric/
    setMethod are collected across files and dispatched on receiver class,
    @ slot access is modeled, and vector names survive t() and
    data.frame() construction.

  • Scope and flow fixes: inherits(x, "cls") guards narrow types,
    useDynLib(.fixes=) prefixes resolve native-routine symbols, R6/S7
    method bodies see self/private/super, top-level
    assign(..., envir = asNamespace(...)) binds, and replacement-function
    assignments (dimnames<- and friends) keep the target bound.

  • User-defined infix operators (%op%) preserve their operands in the AST;
    zeallot/future %<-%/%->% destructuring introduces its pattern
    bindings when a package defining the operator is in scope.

  • Data-driven semantics via new injects stub metadata: withr::with_*
    path injection and R6/S7 method-environment bindings now come from the
    typeshed instead of hardcoded checker logic.

  • Derived NSE for user-defined functions: a parameter whose first use is a
    defusing call (enquo, enexpr, ensym, quo, substitute,
    match.call, ...) marks call-site arguments as unevaluated, so
    arrow-style test helpers (compare_dplyr_binding(.input %>% ...)) stop
    producing unbound-variable noise.

  • testthat helper/setup files now propagate their library()/require()
    attachments (not just bindings) to test files, and the helper filename
    match covers all helper*/setup* prefixes.

  • The data-mask gate is fully data-driven: any loaded package whose stub
    declares eval metadata gets NSE treatment (rlist, patrick, bench, ...),
    and user-defined S3 methods inherit the eval metadata of a stubbed
    generic with the same name (dtplyr/dbplyr verb methods).

  • foreach(i = ..., p = ...) %do%/%dopar%/%op% { ... } binds the loop
    variables in the body regardless of the operator alias used.

  • attach(x) marks the scope's search path as unanalyzable, silencing
    unbound-variable diagnostics for legacy attach-style scripts.

  • Type narrowing applies to expression-position if (e.g.
    x <- if (is.function(f)) f(1) else f).

  • Tidyverse NSE metadata is now GENERATED from installed-package Rd docs
    (gen_nse_metadata.R in r-typeshed reads the <data-masking> /
    <tidy-select> argument markers), giving full dplyr/tidyr coverage and
    a new tidyselect stub; dynamically registered S3 methods inherit their
    generic's NSE metadata.

  • . binds inside data-masked arguments (dplyr do(), pipe idioms), for
    both %>% and the native |> pipe.

  • Defused-parameter derivation covers {{ }} embrace usage and exclusive
    enquos(...)-style ... defusal in user functions.

  • Inside a data-masked argument with an unknown schema, lexically resolved
    symbols infer as opaque — mask columns may shadow them, so their lexical
    types no longer drive arithmetic/comparison diagnostics.

  • Rcpp modeled as a first-class package: sourceCpp() carries the new
    scope_effect: unknown_bindings stub metadata (compiled exports are
    unknowable), cppFunction() returns a function, and base::attach now
    uses the same data-driven mechanism instead of a hardcoded recognizer.

  • tinytest scan context: files under inst/tinytest/ see the package's
    own namespace, tinytest, and DESCRIPTION Depends/Suggests, mirroring
    the testthat context.

Changed

  • RY_NO_INSTALLED_LIBRARIES=1 disables resolution of imported-package
    exports from the machine's R installation; the ecosystem regression
    harness sets it so committed snapshots are environment-independent.
  • The ecosystem harness report writer is implemented in R (jsonlite)
    instead of python3; the harness now requires Rscript.
  • Typeshed auditing and stub generation removed from this repository's CI
    and scripts — they live in r-typeshed, whose CI runs them.

Install ry-cli 0.4.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/sims1253/ry/releases/download/v0.4.0/ry-cli-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://github.com/sims1253/ry/releases/download/v0.4.0/ry-cli-installer.ps1 | iex"

Download ry-cli 0.4.0

File Platform Checksum
ry-cli-aarch64-apple-darwin.tar.gz Apple Silicon macOS checksum
ry-cli-x86_64-apple-darwin.tar.gz Intel macOS checksum
ry-cli-aarch64-pc-windows-msvc.zip ARM64 Windows checksum
ry-cli-x86_64-pc-windows-msvc.zip x64 Windows checksum
ry-cli-aarch64-unknown-linux-gnu.tar.gz ARM64 Linux checksum
ry-cli-x86_64-unknown-linux-gnu.tar.gz x64 Linux checksum