Commit graph

2434 commits

Author SHA1 Message Date
Mick Cooney
57f01ac288 test(r): name the wrapped namespace directive test by its shape 2026-10-18 13:15:09 +01:00
Mick Cooney
fa219a80ad test(r): let the rename verdict helper read <<- and = function definitions
The helper listed only 'name <- function', while the indexer also lists
<<- and = definitions, so a generic defined with = never reached the S3
guard. Add cases that go through the helper with each operator.
2026-10-18 13:15:09 +01:00
Mick Cooney
fa909bec19 fix(r): let the later library() call win when one package is bound twice
rMergeBindings kept the first binding of a node, so a batched call with a
package named by several library() calls was order-sensitive. Keep the
binding whose call starts later, add a unit case and pin the ABA and BAB
shapes through the pipeline.
2026-10-18 13:14:06 +01:00
Mick Cooney
69695dcd67 fix(ci): weight the R rename suite by its measured Windows cost
rename-provider-r.test.ts was charged 100 s in WINDOWS_WEIGHTS_SEC but
takes 600-750 s on the Windows runner (49 persisted fixtures, each a
native analyze). The shard packer therefore placed it on shard 5/10 with
the go and dml-gate suites, which exhausted the 20-minute watchdog on
two consecutive attempts.

Charge it 620 s, add a regression test that keeps it off the go and
dml-gate shards, and raise the recorded-workload ceiling from 17 to 18
minutes: the table total grew by about 520 s, so a perfect ten-way
split now averages about 1047 s per shard.
2026-10-18 01:08:26 +01:00
Mick Cooney
bdaefacd31 Merge upstream main into feat/r-language-support 2026-10-17 22:54:14 +01:00
Gergő Magyar
b961e695ca
fix(devcontainer): refresh Node and streamline development (#3610)
Some checks failed
Devcontainer Smoke / Config-transform unit tests (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (default) (push) Has been cancelled
Publish Development Images / publish (core) (push) Has been cancelled
Publish Development Images / publish (ui) (push) Has been cancelled
Devcontainer Smoke / Build devcontainer image (ui) (push) Has been cancelled
CodeQL / Analyze (python) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Skill copy sync / shipped skills drift guard (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
2026-10-17 21:05:13 +00:00
Gergő Magyar
bd20dde825
fix(taint): report inline and factory handler flows (#3612)
* fix(taint): cover inline and returned handlers (U8)

Join expression-handler CFG summaries to distinct graph functions without changing existing anonymous-owner identities. Keep named function expressions private to their lexical scope.

Related: #3489

* fix(ci): encode inline handler taint paths (U8)

Use codec-safe generated display names while preserving positional function identity. Verify persisted hop continuity and remove the repaired inline-flow accuracy allowance.

* fix: keep anonymous names unbound (U8)

* test: pin inline callback captures (U8)

* ci: run inline taint guard (U8)

* test: align inline handler contracts (U8)

Pin callback-owned call edges while preserving nested callable IDs and module-level HOC attribution. Assert repaired inline flows and the retained captured-closure limitation.

* ci: run native inline proofs on all OSes (U8)

Register real-worker identity/CFG and persisted explain cases on the existing Windows/macOS matrix. Charge explicit scheduling estimates without changing watchdogs or runtime behavior.

* test(taint): reject all enclosing factory sources

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-17 20:22:58 +00:00
Mick Cooney
66a83fe15a Merge upstream main into feat/r-language-support 2026-10-17 21:21:38 +01:00
Mick Cooney
8bbc3bbd4b test(r): narrow the regex and rename-safety results so typecheck:tests passes 2026-10-17 20:10:26 +01:00
Gergő Magyar
30676685e0
fix(rename): apply the exact reviewed edit plan (#3617)
* fix(rename): bind apply to reviewed plans (U7)

Retain exact semantic edits behind single-use preview tokens and reject
source, target, inventory and graph drift before application. Preserve
explicit partial-write accounting and keep large unedited sources covered
with hashes instead of retained text.

Refs #2973

* fix(rename): cover unindexed sources (U7)

Address PR #3617 review by binding every supported source to streamed byte
hashes and canonical targets, including tracked and untracked files absent
from the graph. Keep the exact reviewed edits and bound evidence memory.

Use conventional even-sample medians and extend the existing CI benchmark
to capture and validate unindexed sources without relaxing timing gates.

Note: pre-existing packed Tree-sitter GLIBCXX_3.4.31 host failure in
parse-cache-identity-package.test.ts is not addressed by this PR.

* fix(rename): preserve lazy startup (U7)

Keep language providers off the MCP startup path; load their existing
classification only when rename inventory is requested. The existing
12 startup closure checks now pass.

Address PR #3617 review by including token validation in reviewed apply
timing and growing indexed evidence before preview, preserving the
20MiB controls under a 256MiB native pool. Migrate dispatch and retention
tests to the preview token contract, keeping write-failure oracles.

Note: pre-existing auto-sync /tmp fixture security and installed ARM64
grammar-header failures remain outside this PR; the packed Tree-sitter
GLIBCXX_3.4.31 host failure also reproduces on unchanged main.

* test(rename): always remove apply fixture (U7)

Own the temporary directory from creation in an outer finally so setup
and preview assertions cannot bypass cleanup. Keep spy restoration in
its inner finally and preserve all write-failure assertions.

Address PR #3617 cleanup feedback.

* fix(rename): retain targets and config evidence

* test(rename): verify approved physical output

* test(rename): cover native filesystem matrix

* test(rename): align preview contract fixtures

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-17 19:38:01 +01:00
Gergő Magyar
f43834e814
fix(indexing): converge coverage around directory symlinks (#3616)
* fix(indexing): converge coverage around directory symlinks (U5)

* style(bench): format symlink baseline (U4)

* ci: run directory symlink coverage benchmark

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-17 19:37:45 +01:00
Gergő Magyar
9b04fbe627
fix(embeddings): budget generated inputs and isolate rejections (#3615)
* fix(embeddings): budget generated inputs and isolate rejections (U4)

* test(embeddings): require pending retry request

* fix(embeddings): preserve Unicode in context

* ci: run generated embedding input budget guard

* test(embeddings): run input isolation on native OSes

* fix: retain container identity after long prefixes

* test: distinguish generated input cases

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-17 19:37:30 +01:00
Mick Cooney
ba477524dc Merge upstream main into feat/r-language-support 2026-10-17 19:05:22 +01:00
Mick Cooney
822c6d3fc7 test(r): record that a NAMESPACE-only edit leaves isExported stale until --force
r-provider.md documents this as a limitation of the shared incremental write path. Pin the
current behaviour (stale after an incremental analyse, correct after a forced one) so a fix
there shows up and the document is updated with it.
2026-10-17 18:50:11 +01:00
Mick Cooney
4cc05a0f21 test(r): pin the control flow the CFG visitor documents as not modelled
Control flow inside an expression (y <- if, switch, &&, ||, ifelse, return() or break inside
tryCatch/local), stop(), on.exit() and tryCatch handlers stay one block, and a break or next
outside a loop falls back to EXIT. Nothing exercised these forms.
2026-10-17 18:49:44 +01:00
Mick Cooney
e8701aa956 test(r): pin a cross-file rename, the paste0 gap and the number of R rename cases
The documented claim that a rename succeeds across files had only same-file successes behind
it, and the undetected paste0() lookup was unpinned. Add a qualified pkg::name() cross-file
rename and a run-time-built lookup name, and count the R cases so that an emptied fixture
directory cannot leave the suite green with no test.
2026-10-17 18:49:15 +01:00
Mick Cooney
209653b861 test(r): pin the documented no-edge call shapes and the R6 dispatch limits end to end
Add the r-call-shapes fixture, a package that contains each shape r-provider.md says creates
no edge or node: lapply/Map/do.call/match.fun/get/registry/pkg::f as values, infix operators,
obj@slot, UseMethod/NextMethod, super$ and private$, inherit = pkg::Base, an inherited
self$ call, setClassUnion/setValidity/setReplaceMethod/Class$methods, roxygen @export,
exportClassPattern, renv parsing and the unread import forms. Positive controls sit beside
them, and the whole CALLS set is pinned.
2026-10-17 18:46:33 +01:00
Gergő Magyar
e6ae04ed84
fix(embeddings): reject corrupt identities before mutation (#3614)
* fix(embeddings): reject corrupt identities before mutation (U2)

* fix(embeddings): preserve exact Unicode keys (U2)

* fix(storage): validate identities before opening CSVs

* fix(embeddings): validate caches before mutation

* ci: gate native embedding identity benchmark

* test(embeddings): run identity proofs on native OSes

* docs: describe CSV identity validation pass

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-17 18:45:28 +01:00
Gergő Magyar
c8629f1b6c
fix(storage): preserve published graph on rebuild failure (#3613)
* fix(storage): preserve published graph on rebuild failure (U1)

* test(storage): strengthen publication review coverage (U1)

* test(storage): align publication evidence (U1)

* fix(storage): retain completed recovery archives

* fix(storage): preserve quarantines before cache reads

* ci: run staged publication benchmark guard

* fix(ci): declare WAL replay probe determinism

Document that the bounded recovery read drains and discards its rows. Only WAL replay success is observed; row identity and query behavior are unchanged. The existing 21 query-determinism checks pass.

* test(storage): cover publication across platforms

* test(ci): exclude POSIX suite from Windows estimates

* test: allow native publication retry to finish

* test: verify Windows incremental recovery

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-17 18:45:11 +01:00
Mick Cooney
389365bf57 test(r): pin the whole CALLS edge set of r-package-layout and r-packages
The layout fixture already holds callers in tests/, scripts/, vignettes/ and inst/, but only
their export status was asserted. Pin every CALLS edge, with reason and confidence, for both
projects, so a misdirected edge that keeps the count fails.
2026-10-17 18:44:54 +01:00
Mick Cooney
269a193eca test(r): compare a warm durable-cache run with a cold one
The deferred-owner pass and the NAMESPACE export refinement only ever ran cold in the R
tests. Analyse r-packages and r-package-layout twice, the second time from the saved parse
cache with no worker pool, and require the same definitions (isExported, ownerId) and the
same CALLS, EXTENDS, IMPORTS and member edges.
2026-10-17 18:44:30 +01:00
Mick Cooney
62ad2e4a03 test(r): pin the arity hook, the missing-config paths and the rebuild hint
rArityCompatibility had no test at all. populateRQualifiedCalls and
rFileBelongsToDiscoveredPackage are reached with no package config, and a run must not see
the previous run's answers; neither was asserted. The npm rebuild hint for the R grammar
names the scoped package; nothing checked it.
2026-10-17 18:43:05 +01:00
Mick Cooney
649ed87a6d fix(r): read contains= case-sensitively, and pin the VIRTUAL heritage edge
R matches argument names case-sensitively, so setClass("A", CONTAINS = "B") names no
base; the scope query linked it anyway. Drop the CONTAINS spelling from both predicates.

Pin the VIRTUAL placeholder: a class naming both a real base and VIRTUAL gets both edges
(the dedup set must be seeded only from edges that target the placeholder), the edge keeps
its confidence and reason, and running the heritage hook twice mints one node and one edge.
2026-10-17 18:42:18 +01:00
Mick Cooney
7581ae0697 test(r): pin the documented export-pattern and discovery caps by literal
The caps in r-provider.md were asserted through the constants themselves, so changing a
cap, or the number the document gives, failed nothing. Pin each cap, check that the
document states the same number, and test the state cap, the pattern length cap, the
hidden-package scan budget and a spent matcher at their exact boundaries. Also pin the
backslash-u fallback to the letter u when fewer than four hex digits follow.
2026-10-17 18:40:52 +01:00
Mick Cooney
e7fd13d364 docs(r): say which class owns a setMethod() written with a multi-class signature 2026-10-17 18:00:26 +01:00
Mick Cooney
d9f7ab87ac docs(r): list the call-resolution and class-handling gaps that are left as is
Each statement was reproduced against the code before it was written: self$m()
without a type binding, RefClass sibling calls, calls to generics and
generators, source() suffix matching, the library() edge fan-out, same-named
helpers in two packages, METHOD_OVERRIDES on parameter counts, calls inside
setMethod bodies, inherit = pkg::Base, string-named R6 members, nested function
ids, the TRE differences in export patterns and conditional NAMESPACE bodies.
Also says how a class name shared by several classes is owned and reported.
2026-10-17 18:00:12 +01:00
Mick Cooney
723d9c99e3 refactor(r): state the R export default in the provider, not in shared code
rExportChecker was a shared export that only the R provider used, and it made
export-detection.ts differ from upstream for a function that returns true.
ObjectiveC already writes the same checker inline.

The provider now carries it as () => true with the reason beside it, and
export-detection.ts is byte-identical to upstream again.
2026-10-17 17:57:56 +01:00
Mick Cooney
8b28ef1916 perf(r): read the roxygen block of a function once, not once per formal
extractParameter runs for each formal and re-collected and re-parsed the whole
comment block above the function every time, so work grew as formals x block
lines (2,000 formals under 2,000 @param lines took 11.9 s).

The parsed block is now kept per assignment node in the tree, as the file's
class names already are.
2026-10-17 17:57:25 +01:00
Mick Cooney
7e7e1bc411 docs(r): correct comments that contradict the code
The R provider header said library() and source() bring everything into scope
with no named imports and that imports are standard import nodes: source()
binds no name, NAMESPACE importFrom() produces named imports, and the import
captures come from the R scope query. The scope resolver said a bare member
call never reaches an R5 method and that R has no super, but RefClass methods
call siblings by bare name (unresolved today) and R6 has super$method().
2026-10-17 17:57:20 +01:00
Mick Cooney
8a1cd32b15 chore(r): drop an eslint-disable directive that suppresses nothing
ESLint reports it as unused with --report-unused-disable-directives; no other
file carries it above an ES import.
2026-10-17 17:57:07 +01:00
Mick Cooney
6b5b93ddd4 chore(deps): group the scoped tree-sitter-r grammar with the other grammars
The tree-sitter-grammars group matched tree-sitter-* only, so
@eagleoutice/tree-sitter-r would have been bumped in a pull request of its own
instead of with the other grammars.
2026-10-17 17:57:07 +01:00
Mick Cooney
730cf323f5 docs: say that a new extension point is the one reason to touch shared code
The scope-resolution section says adding a language needs no changes to shared
code. R added two optional LanguageProvider hooks (postParse and
resolveMemberOwnerNode) that no other language implements, so the sentence now
names that case and points at the hook table.
2026-10-17 17:57:07 +01:00
Mick Cooney
937e19878a perf(r): answer package locality from an index of unowned package directories
rQualifierLocality scanned every parsed file path once per distinct pkg::
qualifier, so a run's work was qualifiers x files (4,000 x 5,000 took 0.84 s).

populateRQualifiedCalls now builds the set of <name>/R/ directory names that no
discovered package owns once, and each qualifier is a set lookup. The answers
are unchanged, and a call without the index still scans.
2026-10-17 17:55:00 +01:00
Mick Cooney
35edbb3515 fix(r): stop a call through a formal or loop variable at that name
A formal or a for variable bound no name in the scope model, so
function(data, transform) transform(data) was credited with a call of a
top-level transform() at 0.85, and the unique-name fallback made the same guess
across files.

Each function scope now publishes the names its formals and for loops bind as
lexical names, the shadowing barrier the scope model already has for a
parameter. A formal with a constant default that the body never assigns is left
out, because R skips a value that is not a function when it looks up a callee
(the rename provider reads it the same way).
2026-10-17 17:53:56 +01:00
Mick Cooney
32fb2973d8 fix(r): let the package attached last mask a name two library() calls define
Two library() calls that attach packages defining the same name bound the call
to whichever package file sorted first by id, so a script that attached lp2
after lp reached lp's function while R reaches lp2's.

rMergeBindings now keeps, within the wildcard tier, only the bindings of the
library() call that comes last in the file. A package of several files that
defines the name twice stays ambiguous, and a binding with no call position
leaves the tier as it was.
2026-10-17 17:43:52 +01:00
Mick Cooney
d3e06d373d fix(r): refuse a closure-local function as the unique-name fallback target
A function bound inside another function lives in that call's frame, so no
other function in its file or another can call it by its bare name. The
unique-name fallback ignored that: when the name was unique in the repository,
a bare call elsewhere bound to the closure-local function at 0.5.

The R fallback veto now refuses a function or class that the file's scope
model holds below the top level. A candidate the scope model does not hold, and
every top-level definition, are judged as before.
2026-10-17 17:39:52 +01:00
Mick Cooney
23eb44dd13 fix(r): look up backticked NAMESPACE exports from a set built once
Resolving an importFrom() name that the provider's NAMESPACE does not export
walked every export entry before giving up, so a provider with 20,000 exports
and a caller importing 20,000 names took about two seconds, and NAMESPACE text
is the only bound.

The walk only looked for entries that start or end with a backtick (a quoted
"`%+%`" entry keeps them). Those are now collected in one pass per export set,
and a lookup is a set membership test; the answers are unchanged.
2026-10-17 17:34:23 +01:00
Mick Cooney
055466b2ed fix(r): keep installed libraries and ignored trees out of package discovery
Discovery read the disk on its own and skipped only renv, packrat, revdep and
.Rcheck, so an installed library under lib/, library/, .Rlibs/, vendor/ or
build/ registered its packages as repository packages (their qualified calls
then fell to the name-based guess instead of reading as external), and a tree
of ignored directories spent the 5,000-directory hidden-package budget and
made discovery read as incomplete.

Discovery now skips dot-directories and the directories the indexer itself
ignores (isHardcodedIgnoredDirectory), and a DESCRIPTION with a Built: field,
which only an installed package has, registers nothing and hides nothing. The
depth-3 and 200-directory bounds are pinned with their boundary values.
2026-10-17 17:30:04 +01:00
Mick Cooney
f52571aac8 fix(r): name an S3method() of another package's generic after the generic
S3method(pkg::generic, class), the delayed-registration form roxygen2 writes
for @exportS3Method pkg::generic, registered no method at all when written
bare and the nonexistent "pkg::generic.class" when quoted, so the function
generic.class read as not exported and an importFrom() of it did not bind.

The generic is the text after the last :: or ::: outside quotes and
backticks, so the method is generic.class as R registers it.
2026-10-17 17:27:35 +01:00
Mick Cooney
1ce05069e9 fix(r): charge export patterns that are refused after their NFA was built
A pattern that expanded past the state cap was refused only after building up
to 16,384 states, yet it reserved no states, charged no work and did not close
the allowance, so the next such pattern started from scratch: 98 KB of
exportPattern("(a|b){8000}...") lines took 4.2 s while the budget counted
nothing, against the documented bound of about a second.

A refusal is now charged for what was built (eight visits per state or build
step), and a spent work budget refuses later patterns before they are parsed.
The guide also says that budget exhaustion while resolving importFrom() names
is not reported, because the scope resolver loads its own configuration.
2026-10-17 17:26:24 +01:00
Mick Cooney
9944a7d969 fix(r): charge a remembered export-pattern verdict to the work budget
A matcher answered a name it had already decided from its memo without any
charge, so asking thousands of matchers about the same few names (initialize,
print, show across many classes) cost time that no budget counted: 5,000
repeat calls over 12,000 matchers took 612 ms and charged nothing.

A memo hit now costs one visit, so the repeat is bounded by the shared budget
like every other cost, and the verdict itself is unchanged.
2026-10-17 17:26:00 +01:00
Mick Cooney
7808a1bdb4 fix(r): make the export-pattern nesting limit independent of JIT warm-up
MAX_NESTING was 2048, beyond what a cold process can recurse: a deeply nested
pattern failed with a stack overflow in a fresh process and compiled after a
warm-up, so whether a NAMESPACE pattern was in effect depended on the machine
and on what ran before it, and the reason given was misleading.

The limit is now 256, which still compiles on a quarter of the default stack,
so the verdict is the same cold or warm and carries the right reason.
2026-10-17 17:25:25 +01:00
Mick Cooney
13cdf4347a fix(r): own a setMethod() written with signature() or c() by its first class
setMethod("show", signature("A"), ...) and c("A", "B") signatures named no
owner, so the methods of one generic on different classes collapsed into one
node and none had a HAS_METHOD edge.

The owner is the string in the first argument of a signature() or c() value,
named or not. A multi-class signature therefore belongs to its first class; a
first argument that is not a string still names no owner.
2026-10-17 17:20:40 +01:00
Mick Cooney
b107810fcc fix(r): attach members to a class whose name is shared or defined twice
attachDeferredROwners left every member of a class unowned when more than one
class of that name was registered, including a class defined twice in one file
(one graph node registered twice) and the same helper class in R/ and tests/.

Candidates are now de-duplicated by node id. When several distinct classes
share the name, the member attaches to the one defined in its own file; if its
file defines none or several of them the member stays unowned, as before, and
one warning per call names the class names affected.
2026-10-17 17:17:12 +01:00
Mick Cooney
7730f6df3d docs(r): list the final rename refusals and the PDG answers R cannot give
The rename section now names each refusal the provider makes (NAMESPACE export
read from the indexed flag, infix functions and operators, replacement functions,
new-name collisions and value reads, group and common generics). The control-flow
section records that pdg_query flows, explain and impact --mode pdg return empty
answers for R without saying that R has no def/use harvest; the shared tools carry
no per-language capability signal, so this is documented rather than patched.
2026-10-17 17:03:59 +01:00
Mick Cooney
66dd31b606 test(r): run the rename guard unit tests on a real knowledge graph
Four unit files handed checkSafety a graph whose forEachNode did nothing, so the
NAMESPACE branch could never be reached from them. They now build a real graph
(empty, or with one node of a chosen label for the member-versus-function case).
2026-10-17 16:57:41 +01:00
Mick Cooney
0bcd54d289 fix(r): refuse a new name that registers a method of a group or common generic
BASE_GENERICS missed the S3 group generics and many dispatching base functions, so
sum.items, Ops.money, Math.money, abs.diff, is.numeric.foo, as.complex.foo and
unlist.foo were accepted and silently registered as methods.

The list now has the Ops, Math, Summary, Complex and matrixOps groups with each
member, the is.* and as.* generics, and the common base, stats, graphics and utils
generics. Unit tests walk the documented group members, and two fixtures cover
Ops.money and sum.items.
2026-10-17 16:57:20 +01:00
Mick Cooney
dd4daefa3f fix(r): refuse a new name that the sources already read as a value
The engine protects the call form of a new name, but R's reference sites are calls,
so a plain read was invisible: sapply(x, paste), Reduce(max, x), x <- letters or a
default f = assist all kept compiling and silently started to mean the renamed
global.

The provider now refuses any identifier read of the new name that is not bound
locally: a parameter of an enclosing function covers a read, and covers a call only
when it has no default. Bound names, argument names, members ($, @) and qualified
names are not reads. Four fixtures and unit tests cover values, callees, formulas
and the allowed spellings.
2026-10-17 16:54:35 +01:00
Mick Cooney
cacfc47921 fix(r): refuse a new name that is already a global
assertDeclarationCollisions is per file and function-only, so a rename onto a name
that a top-level variable already held (or assigned later) overwrote or was
overwritten by it, and a function of that name in another file became a second
global whose survivor depended on load order.

The provider now refuses a new name that is assigned at top level (<-, =, ->, a
for variable), assigned with <<- or ->> anywhere, or defined as a function in
another file. Local assignments, parameters and members of that name are still
allowed. Five fixtures and a positive control cover each form.
2026-10-17 16:52:13 +01:00
Mick Cooney
90fb41c1fc fix(r): refuse a rename that would strand a replacement function
helper(z) <- 5 calls the replacement function helper<-, not helper, so renaming
the getter renamed the call in the assignment target and left the setter
unreachable under the new name, and renaming helper<- itself left every
helper(z) <- v calling a function that no longer exists.

Refused now: a call of the name anywhere in an assignment target (plain, nested
in $, [[, names(), attr(), or the -> forms), an old or new name ending in <-, and
a string-defined setter of either name. Four fixtures and a unit test cover each
form beside calls that are only read.
2026-10-17 16:50:05 +01:00