Commit graph

1756 commits

Author SHA1 Message Date
Mick Cooney
fdbb64c920 fix(r): refuse a rename of a function that a roxygen2 @export tag exports
roxygen2 rebuilds NAMESPACE from @export* tags, so renaming a tagged function
changes the package's public API on the next document() even when the NAMESPACE on
disk and the persisted isExported say it is private. checkSafety now refuses a
top-level function whose directly preceding #' block has an @export* tag. Documents
that a successful rename leaves .Rmd/.qmd/.Rd/pkgdown/roxygen cross-reference
consumers unchanged; the rename result has no provider-owned note field.
2026-10-18 15:53:42 +01:00
Mick Cooney
1f7f7135f5 fix(r): load the R grammar lazily so a native load failure disables only R
parse-worker.ts and languages/r/query.ts imported @eagleoutice/tree-sitter-r
statically. A binding that cannot load (no prebuild, ABI mismatch) therefore
crashed every worker and every module importing the R provider, failing analyze
for all languages. The worker now loads it behind the same guard as the vendored
grammars, through the shared grammar loader, and the query module resolves it on
first use as the Kotlin one does. With the binding removed, analyze indexes the
other languages and skips R files with one logged error.
2026-10-18 15:51:58 +01:00
Mick Cooney
86ec528225 fix(r): make rename refuse when the NAMESPACE reader may have hidden an export
The NAMESPACE scanner skipped conditional (if/else) bodies, dropped raw-string and
multi-line arguments, and discarded a malformed directive together with everything up
to the next column-0 directive, all silently. Rename treated 'not in the export lists'
as proof of privacy, so it could rename a function whose export() entry the reader had
never seen and leave NAMESPACE naming a missing function.

The scanner now reports when a skipped or discarded region names an export-family
directive, and parseRNamespaceExports also flags an argument of one that it cannot
read. loadRPackageConfig stores that as RNamespaceInfo.exportsUnreadable and rename
refuses with incomplete_semantics. An ordinary NAMESPACE, including conditional
imports, does not set it. The indexed isExported is unchanged. r-provider.md updated.
2026-10-18 15:45:51 +01:00
Mick Cooney
5d4239e690 fix(r): record the rename NAMESPACE config before the populate passes replace parsed files
populateRNamespaceImports and populateRQualifiedCalls replace entries of the
ParsedFile array (import synthesis; any dropped external pkg::fn site), but the
rename guard looks the recorded config up by the original ParsedFile object. A
package file that called stats::sd(), or whose package importFrom()ed a name from
another repository package, therefore read as 'NAMESPACE was not read' and every
rename in it was refused with incomplete_semantics.

Record the config against the objects as received and again against the final
array. Adds rename fixtures for a private and an exported function in a file
with an external qualified call, a private function in a package with a local
importFrom(), and extends the incremental NAMESPACE test with an external call
and an incremental run that reuses the parse cache.
2026-10-18 15:39:42 +01:00
Mick Cooney
7eaaec0384 fix(r): make rename re-read NAMESPACE and fail closed on a stale export flag
After an edit of NAMESPACE alone, the unchanged R/ files are not
re-processed, so the persisted isExported of a newly exported function
stays false and rename read that as proof of privacy. The rename guard now
also uses the NAMESPACE the planner has just loaded from disk (recorded by
the R workspace hook against each parsed file): it refuses when NAMESPACE
exports the name by export(), exportPattern() or S3method(), and fails
closed (incomplete_semantics) when NAMESPACE is gone or unreadable, when
an exportPattern() was dropped or ran out of budget, or when no config was
recorded. The persisted flag remains a second, additive check. The
incremental test now asserts the refusal on the stale index and after
--force; unit cases cover each way NAMESPACE can change. Docs updated.
2026-10-18 15:08:46 +01:00
Mick Cooney
342523965f fix(r): keep a repeated library() package at its first attachment
R leaves an already attached package where it is, so library(a);
library(b); library(a) still lets b mask a. The merger counted the last
call as a fresh attachment, and because the pipeline folds the bindings of
a name in one at a time, a package masked at one step was forgotten at the
next. Remember each package's first attachment, masked ones included, on
the surviving bindings, and flip the expectations that pinned the other
direction (A/B/A -> B, B/A/B -> A) in the unit and pipeline tests.
2026-10-18 14:39:28 +01:00
Mick Cooney
f62877505b Merge upstream main into feat/r-language-support 2026-10-18 13:46:32 +01:00
Abhigyan Patwari
39e0ced68d
test(server): make the opaque repo id assertion deterministic (#3625)
`publicRepoId` is an HMAC under a key drawn once per process
(`randomBytes(32)`), so the 22-character base64url id is effectively
random on every run. The test asserted `not.toContain('ws')` to show the
id does not leak the path, but any id contains "ws" by chance with
probability 21/4096 (~0.5%). CI hit it on #3624 with
`gt5f4oNtpcwsi6vep8lzQN`.

Across 20,000 fresh keys the old assertion failed 118 times and the new
one 0 times. The test now checks the base64url shape and that the full
path is absent; '/' is outside the alphabet, so a chance match cannot
happen.

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:43:33 +01:00
Mick Cooney
3cfd55a340 fix(r): say what is wrong with a rejected rename name
validateName let ... and ..N through to a misleading name_collision. Reject
them as reserved, say in the message that new names are ASCII-only by design,
and report an old name made only of non-ASCII characters as an unsupported
identifier rather than an infix function or operator.
2026-10-18 13:20:03 +01:00
Mick Cooney
587bf6ce85 fix(r): read a NULL default as a formal that may hold a function
A formal defaulting to NULL is the usual placeholder of an optional
callback, so classing it as constant let a rename edit the call of the
parameter and drew a false edge to a same-named top-level function. Treat it
as 'maybe': the rename refuses the call and the scope publishes the formal
as a lexical name. Update the provider guide and pin both paths.
2026-10-18 13:18:37 +01:00
Mick Cooney
af4ee3d9b4 fix(r): keep a matched export-pattern answer after the work budget is spent
rExportPatternMatches answered false for every name once the budget was
spent, so a duplicate definition of a name that had been exported earlier
flipped to private. Remember the names matched while the budget lasted and
answer them from that set; undecided names still read as unexported, which
the documentation now says.
2026-10-18 13:17:17 +01:00
Mick Cooney
a01ce7940e fix(r): keep the whitespace inside a quoted NAMESPACE name
normaliseArg trimmed the text inside quotes and backticks, so export(" a ")
named the symbol a instead of the symbol ' a ', which is what R reads. Trim
only around the argument, and pin it with a parser test and a pipeline case
where a package defines both a and ` a `.
2026-10-18 13:16:02 +01:00
Mick Cooney
e25a1aee8f test(r): assert the pattern length cap accepts a pattern of exactly that length
Build the pattern from empty groups so only the length rule is in play, and
check that it compiles, matches 'aa' and not 'a', and that one more
character is refused.
2026-10-18 13:15:10 +01:00
Mick Cooney
214e7f8e68 test(r): tell the R6 owner walk from a non-class with a discriminating assertion
getRTopLevelMethodOwnerName is the setMethod helper and returns null for any
argument node, so the negative assertion could not fail. Use the owner lookup
the R6 path uses, in the positive and the negative case.
2026-10-18 13:15:10 +01:00
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
Gergő Magyar
17b056fc79
test(lbug): guard max DB size accounting across repeated checkpoints (#3529) (#3624)
On LadybugDB 0.18.3 (1.6.12, 1.6.13-rc.57) every CHECKPOINT left its
shadow file handle's frame groups charged against max_db_size until the
database closed (LadybugDB #924, fixed by #925 before 0.21.1): about
8-9 MiB per checkpoint, empty ones included. Analyze checkpoints every
5 s and per embedding window, so a long run can report "Maximum database
size ... has been reached" while the file is small, and a retry in a
fresh process succeeds.

The new native test opens the database through initLbug with a 256 MiB
ceiling and issues 128 driver-path checkpoints (tryFlushWAL). It fails
on 0.18.3 with the reporter's message and passes on 0.21.1, so a future
engine pin that reintroduces the leak is caught. Registered in the
lbug-db project and the cross-platform native list.

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:09:16 +01:00
Gergő Magyar
18128f9347
test(embeddings): cover CodeEmbedding scans after delete and re-create (#3569, #3474) (#3623)
LadybugDB 0.18.3 (shipped in 1.6.12 and 1.6.13-rc.57) misreads string
columns after a selective write. The CodeEmbedding table is affected by
the embedding writeback's own statements, without any COPY: after
per-node DELETE + CREATE and a checkpoint, 3,936 of 8,192 tuples scan
wrong and the {nodeId} lookup misses a retained row. On a released
build this makes unchanged nodes look new (re-embedding) and puts blank
or truncated ids into the CodeEmbedding key ("duplicated primary key").
The 0.21.1 engine on main fixes it.

- reproduce-search.cjs gains an `embedding` scenario using the
  production CREATE / DELETE-by-nodeId statements; it fails on 0.18.3
  and passes on 0.21.1. The vitest wrapper runs it with the other
  scenarios; per-scenario expectations are now data, not branches.
- incremental-method-integrity runs both analyses with a deterministic
  loopback embedding endpoint and asserts that only the edited file is
  re-embedded and every other CodeEmbedding row (4,096+) is unchanged
  from a cold reader.

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:01:04 +01:00
Gergő Magyar
5d1bc3dcea
feat(eval): request PR cost comparisons by label (#3622)
* feat(eval): measure PR cost before merge

Reuse authenticated historical MCP measurements for one pinned PR preview. Keep the protected main evaluator and publish separate evidence that cannot authorize a release.

* fix(eval): gate PR cost runs on a label

Require a repository writer label event and retain the opt-in label
and exact PR head before dispatch and paid sessions. Keep the
request handler on trusted base code and execute evaluation on main.

* ci: document trusted cost label trigger

* fix(eval): restrict previews to maintainer labels

Remove manual PR preview inputs and authorize Maintain/Admin roles before calling the protected reusable evaluator. Compare workflow measurement settings with historical evidence instead of exempting the entire workflow.

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
2026-10-18 11:00:08 +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
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
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
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