Since the hosted runner image changed on 2026-11-11 the MSan lane aborts
before any test runs:
MemorySanitizer: CHECK failed: msan_linux.cpp:193
"((personality(old_personality | ADDR_NO_RANDOMIZE))) != ((-1))"
The runner kernel now randomizes mmap with more bits than MSan's shadow
layout tolerates, so the runtime tries to re-exec itself with
ADDR_NO_RANDOMIZE; inside the lane's container the default seccomp
profile refuses that personality() call. The last green MSan run was
2026-11-11 01:28Z (run 42939151247); every PR run with the lane since
18:45Z failed this way (runs 43080749803, 43081615012), independent of
the change under test.
The TSan leg hit the same kernel behaviour earlier and lowers
vm.mmap_rnd_bits to 28 on Linux before its suite. Do the same on the
host before the MSan container starts; the container shares the host
kernel, so MSan's layout fits again without disabling ASLR in the
container. `|| true` keeps the step from failing where sysctl is not
permitted, exactly as in the TSan step.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- registry_test_double_path_table: the anchored predicate's positives
(fake, mock, mocks, testing, testutil directory segments; mock_*.go,
fake_*.go) and negatives (inspect, latest, contest, fakeroot, mockery,
testingutil, mocking.go, mock_store.py, a file named fake, a different
case, NULL).
- registry_swift_overload_choice_uses_stored_verdicts: the Swift overload
choosers score with the verdicts stored at registration; without them the
smaller QN, a test double, wins the tie.
- pipeline_registry_ranks_by_extractor_verdict_full_and_incremental: the
`latest` vs `_test.go` misranking the QN scan made, through a full index,
a closure-repair incremental run and a forced legacy-partial run.
- pipeline_registry_ranks_test_doubles_last_full_and_incremental: a
generated fake that out-ranks the real package on proximity, through the
same three runs.
Each fails with the piece it pins reverted (predicate, seeder verdict,
incremental seeder rule, closure-repair verdict carry, Swift verdict carry).
Co-authored-by: Chad <4307099+BobbieBarker@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The incremental registry seed reads each stored node's test verdict back
with cbm_node_is_test, as the full build takes CBMDefinition.is_test. Main's
closure-repair route seeds that registry from cbm_delta_preseed proxies,
which load only the properties a resolver reads, and is_test was not among
them: every proxy scored as production, so a re-resolved caller could bind a
test helper the full build ranks last. On gardener a comment-only edit of
three files made the incremental index differ from a fresh full one by two
ranking edges (a WRITES into a _test.go function, a USAGE into
pkg/utils/test); with this change the only differences left are the 13
similarity / OVERRIDE / IMPLEMENTS edges main already shows on that edit.
The preseed now selects whether a row's properties carry "is_test":true and
adds just that member to the proxy's properties. Proxies are never written
back. If adding it cannot be allocated, the preseed fails like an id
mismatch does, rather than seed a proxy that would rank as production.
Co-authored-by: Chad <4307099+BobbieBarker@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The extractor's verdict is the right primary signal, but Go projects keep
many test doubles in ordinary, non-test files: generated fake clientsets,
mock packages, mock_*.go files. The QN scan this branch removed happened to
rank those below real code; the verdict alone does not, so calls from
production code started resolving into them (gardener: about 570 call edges
newly landed in fake or mock files, 73 of them from production code).
cbm_registry_test_double_path is one registry-only predicate: a directory
segment named exactly fake, mock, mocks, testing or testutil, or a file
named mock_*.go or fake_*.go. It is anchored to whole segments and the
file-name prefix, so inspect, latest, contest, fakeroot or mockery never
match. All three registry seeders pass the extractor verdict OR this
predicate on the path the definition's node stores (pass_definitions.c,
pass_parallel.c, and the incremental seed in pipeline_incremental.c), so a
full and an incremental index rank alike. register_and_link_def now takes
the file's rel path for that, as pass_definitions.c already has it.
The rule only orders candidates when a call is resolved. It stays out of
cbm_is_test_path and cbm_is_test_file, so the stored is_test property,
TESTS edges and importance do not change.
Co-authored-by: Chad <4307099+BobbieBarker@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
index_under_name, swift_choose_candidate and the fuzzy path's
filter_import_reachable call gained parameters in the merge of main; this
re-wraps exactly those lines the way clang-format (LLVM 23) lays them out.
No code change.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings the branch up to main f5d57465f. Conflicts and how each was resolved:
- pipeline.h: registration now goes through cbm_registry_add_lang_in_module
with cbm_registry_add and cbm_registry_add_lang as thin wrappers. The
extractor's is_test verdict is threaded through all three as their last
parameter (the convention this branch set for cbm_registry_add), and main's
doc comment on cbm_registry_add is kept with the branch's verdict note.
- pass_definitions.c, pass_parallel.c: main's registration block (generated
members -> cbm_registry_add_qn_only, Swift signatures) is kept and
cbm_registry_add_lang_in_module receives def->is_test.
cbm_registry_add_qn_only needs no verdict: those entries never reach the
by-name buckets the scorer reads.
- pipeline_incremental.c: main's registry_seed_t visitor is kept and passes
cbm_node_is_test(node), so the incremental seed gives the same verdict as
the full index.
- registry.c includes: both foundation/arena.h (main) and foundation/log.h
(branch).
- registry.c index_under_name: takes both the verdict and main's language.
The branch's drop-on-failed-growth rule now covers the lang side array
too, since it shares is_test_cap: an entry that cannot get both bytes is
dropped and logged (registry.verdict_alloc_failed).
- registry.c best_by_import_distance: the branch's unconditional verdict
read plus main's optional lang_flags tie-break input.
- registry.c resolve_multi_with_imports: the filtered view always copies
both side arrays (no "flags may be absent" case is left).
- registry.c cbm_registry_fuzzy_resolve: the branch's
filter_import_reachable fix, now carrying the languages along with the
verdicts, so the filtered subset is scored with its own verdicts and
tie-broken with its own languages.
- registry.c cbm_registry_add body: main's layout (the body lives in
cbm_registry_add_lang_in_module, cbm_registry_add is a wrapper) with the
verdict passed to both index_under_name keys.
- tests/test_registry.c: main's renamed `proximity` registry (Windows
defines `near`) with the verdict argument.
Semantic follow-through in code main added since the branch's base (each
relied on "no verdict means ask is_test_qn", which this branch deletes):
- qn_lang_flags, candidates_without_members and registry_entry_language
no longer guard against side arrays that cover only part of a bucket:
under the drop rule every entry of a non-empty bucket has both.
- The Swift overload choosers (swift_choose_receiver,
swift_choose_candidate) passed NULL verdicts. cbm_registry_swift_candidates
now copies each match's stored verdict from its by-name bucket into a
parallel byte array (same allocation as the match lists), and the choosers
carry it through their receiver and import filters, so every
best_by_import_distance call scores with stored verdicts.
- Test call sites main added (test_registry.c 74 cbm_registry_add,
17 cbm_registry_add_lang, 9 cbm_registry_add_lang_in_module;
test_pipeline.c 6; test_callable_sig.c 3) pass `false`, as the branch's
own call sites do; only those lines were re-wrapped.
is_test_qn has no remaining definition or call site.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Three changes to how PR CI consumes the test-impact selection (user
decisions R150 and R151, 2026-11-11).
Fork PRs. select-tests ran only when vars.TEST_IMPACT_MODE was `gate`, and
GitHub passes no repository variables to a pull_request run from a fork:
all 24 fork runs in the last 40 PR runs skipped the job, maintainer-started
ones included. The condition is now (vars.TEST_IMPACT_MODE || 'gate') ==
'gate'. Setting the variable to anything else (e.g. `shadow`) still turns
the gate off for same-repository and Dependabot PRs; for fork PRs the off
switch is the default in the expression on main. No pull_request_target,
no new permissions, no secrets.
Base tooling. select-tests built and ran the PR checkout's own engine,
test-impact-select.sh and selection.py, so a change to them could narrow
its own tests. The job now checks out the merge base (HEAD^1 of the merge
ref) into a worktree, builds that engine and runs that copy of the script
with --repo pointing at the PR checkout. test-impact-select.sh separates
the tooling (its own checkout) from the repository under test (--repo).
Until this lands, a PR's merge base has no --repo: the job then fails and,
as before, every test runs.
All legs. Only the Linux x86_64 test leg consumed a selection. A narrowed
answer now also produces portable/ (selection.py --whole-suites with the
platform-specific suites of the base and HEAD), and the arm64 and macOS
test-unix legs and the Windows leg run that view: platform-neutral suites
as selected, every platform-specific suite whole. No portable view, no
narrowing there. Sanitizer, TSan, MSan, diag and LSan lanes still run
every test. The shard harness already slices the selected list, so the
sharded arm64 leg and the shard-completeness proof need no change.
docs/SMART_CI.md and README.md follow; tests/test_lane_wiring_contract.sh
pins the new wiring.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
selection.py learns two things the CI needs to narrow test legs other than
Linux x86_64, where the coverage map is recorded:
- `platform-suites --repo DIR --rev REV...` lists the platform-specific
runner suites: those whose defining test file holds a SKIP_PLATFORM( call
or an #if/#ifdef on an OS or architecture macro (the two markers
scripts/check-no-test-skips.sh names for a genuinely platform-specific
test), and those tests/test_main.c registers under such a conditional.
Every revision given is read and the results are united, so a change
cannot declassify a suite by dropping its conditionals. Any git failure
exits 1.
- `filter --whole-suites FILE` writes the view for such a leg: every listed
suite runs whole there, its per-test tokens collapsed to the suite; a
listed suite the answer did not select is added and marked optional (a
build that does not register it drops it with a notice, as a full run
never runs it); the answer's own required suites stay required. An
unusable list runs everything.
Nothing consumes the new view yet. Contract cases in
tests/test_test_impact_selection.sh.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The test runner's __cbm_rss_tree_root role starts a __cbm_rss_tree_hold
child, and no test names __cbm_rss_tree_hold itself: the test that spawns
the root does. The role lift excludes every handler from its literal scan
(a handler naming its own literal is no spawner), so a change reaching the
hold handler found no test naming it and vetoed the whole selection with
RUNNER_REACHED. In the last 40 PR runs that veto decided 14 of the 39
engine answers, every one on __cbm_rss_tree_hold.
A handler that names another role's literal starts that role. Reaching the
started role now reaches its starters too: their literal scan selects their
spawners, and the started role counts as named when a starter does. A role
that a test names directly and that another role also starts now selects
both sets of spawners; before, only the direct ones ran. Runner files that
define no suite are read for starters as well, since handlers live there.
The RUNNER_REACHED note for a role now carries the call chain from the
handler to the changed code, as the main-reach note does, and names the
role it starts when the veto comes from a starter.
Test: test_impact_engine_role_started_by_a_role_selects_its_spawners
(before the fix: run_all, "the change reaches the test-runner role
__fx_hold_role, which no test names").
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Adds a native Windows guard for #654: a successful MCP initialize +
tools/list exchange, run against an isolated cache and runtime directory,
must leave nothing on stderr but the server's own structured log lines.
- tests/windows/test_mcp_stdio.py: the guard. The English and Spanish
path-not-found messages reported in #654 are named cases; any other
non-empty stderr line that is not a structured "level=<name> msg=<event>"
record fails as well, so the same leak on a machine in another language
is caught without listing its translation. The log format is pinned to
text so an inherited CBM_LOG_FORMAT=json cannot change the verdict. Cache
and runtime live under one temp root, so both are removed with it, also
on failure, after the daemon is stopped.
- tests/windows/mcp_stdio.py: close() joins the stderr reader, and the new
stderr_complete() reports whether the reader reached EOF. The guard reads
stderr only after it has; a reader still running after the server exited
is a SETUP failure (exit 2), never a verdict on a partial capture.
- scripts/test-windows.ps1: registers the guard in the green guard list
and documents it in the header.
#2213 was an earlier duplicate of the same check.
Refs #654.
Distilled from #2217.
Co-authored-by: lorenzozanee <wyz0707@proton.me>
Co-authored-by: Shinken009 <182390694+Shinken009@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings the branch up to date with main 6d34ca15. Two resolutions:
- tests/test_parallel.c (textual conflict): main added
parallel_csharp_nested_type_and_global_namespace_binding and
parallel_csharp_parse_damaged_namespace_is_not_global at the same spot
as this branch's parallel_csharp_keyword_receiver_no_name_guess_issue2121.
Resolved as main's file plus this branch's own hunk (all three tests
and their RUN_TEST lines kept); the zero-context patch-id of the
branch's change to the file is unchanged.
- src/pipeline/registry.c (semantic conflict; merged cleanly as text but
would not compile): main's cbm_registry_swift_candidates (#2436) calls
receiver_chain_admits with two arguments, and this branch gives it a
third, the calling file's language. The Swift caller now passes
CBM_LANG_SWIFT. Behaviour is unchanged: the type-keyword branch applies
to CBM_LANG_CSHARP only.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Two repositories whose paths differ only in a non-ASCII segment (for
example /Users/dev/<CJK name A> and /Users/dev/<CJK name B>) must derive
distinct project names. Before the #571 fix every non-ASCII byte was
mapped to '-', so both collapsed to the same name and shared one index.
The existing #571 test pins one exact string; this test pins the
property that two different segments stay distinct and validator-safe.
Hardening on top of the original test: each name must also contain the
hex image of its own segment (e5bc80e58f91 / e5908ee7abaf), so a future
mapping that keeps the names distinct without preserving the segment is
caught as well.
Refs #571
Distilled from #2219.
Co-authored-by: lorenzozanee <wyz0707@proton.me>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The frontend maintenance monitor probes for a maintenance request on
every cycle, and each probe try-acquires the maintenance marker lock (on
Windows that revalidates the whole ancestor directory chain). The quiet
no-maintenance cadence was raised from 10 ms to 250 ms because the 10 ms
loop burned a large share of a core for the life of every MCP session,
but nothing pinned that cadence: a regression back to the hot loop would
pass every suite.
The monitor now sleeps on a local idle_ms in its ABSENT branch and, under
CBM_ENABLE_TEST_SEAMS only, records that exact value immediately before
sleeping on it (cleared by cbm_daemon_frontend_test_observer_reset). The
new test starts an idle monitor, waits until a value is recorded, stops
the monitor and asserts the recorded sleep is at least 250 ms. Once set,
the recorded value is stable, so the verdict depends on neither elapsed
time nor scheduler cadence; the deadline is only a hang guard. Production
builds carry no seam code and sleep the same 250 ms as before.
The original test counted presence observations inside a 100 ms window,
which asserts a transient timing window; it is replaced by the recorded
cadence above.
Refs #2093
Distilled from #2215.
Co-authored-by: lorenzozanee <wyz0707@proton.me>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
`dynamic` is a contextual keyword, not a reserved type keyword like
`int`. The dynamic type has no static members, so a `dynamic.X`
receiver is a variable named `dynamic` (or a user type spelled
`@dynamic`), never the built-in type. On the keyword list it sent such
a call through the receiver chain check and refused it.
Drop `dynamic` from cs_type_keyword_root. `nint` and `nuint` stay: they
name the built-in type in receiver position. On dotnet/runtime all 586
`nint.`/`nuint.` member accesses are static members of the built-in
types, while none of the 10 `dynamic.` receivers is the built-in type.
registry_receiver_chain_csharp_dynamic_variable_still_binds_issue2121
pins it: a C# call through a variable named `dynamic` still binds. It
fails with `dynamic` on the list and passes without it.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Conflicts resolved:
- src/daemon/runtime.h: both sides add test seams at the end of the
CBM_ENABLE_TEST_SEAMS block. Kept main's connection-cap and HELLO-order
seams and placed this branch's fingerprint-cache seams after them; the
branch's added lines are unchanged.
- src/cli/hook_augment.c: kept main's re-arm guard, which opens the
timeouts log only once and keeps that descriptor. Dropped main's early
return and breadcrumb formatting after the open: this branch formats
the breadcrumb before the log path is resolved, so stderr gets it even
when the log cannot be opened. That formatting runs on every arm, so
main's re-arm comment now says the text above is refreshed, and the
#858 header comment now says the breadcrumb also goes to stderr.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The fingerprint cache sat in every fingerprinting acquire, including
peer verification. Limit it to the process's own image: every acquire
site now passes the process id to the shared resolve step, which marks
a peer's key invalid, so a peer is never looked up, never committed,
and is hashed on every fingerprint. Re-hashing of verified peer copies
is handled by the runtime service's retained peer references (#2335).
The daemon's saving is unchanged: it hashes its own image once at start
instead of twice.
A process has one own image, so the cache drops from 8 entries to 1,
which removes the slot search and the round-robin eviction.
The cache comment no longer claims that SetFileTime cannot reset the
Windows change time; SetFileInformationByHandle can set it. Limited to
the own image, the key does not rely on it: Windows keeps a running
image locked against modification for as long as the process, and so
the cache, exists.
Test: daemon_runtime_process_fingerprint_caches_only_own_image spawns
this runner as a peer, whose image has the same file identity as the
own image, and asserts that two peer fingerprints cost two hashes, two
own fingerprints one more, and a later peer fingerprint one more again.
Before this change the second peer fingerprint was served from the
cache.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The vendored vbnet scanner lexes a whole VB XML literal as one external
token, and the parser offers it a '<' wherever a declaration or member
starts. There, in valid VB, '<' opens an attribute block
(`<Serializable()> _`), never an XML literal. The scanner nevertheless
took `<Serializable` for an element start and read the rest of the file
looking for a closing tag before giving up: O(rest of file) per
attribute, O(n^2) per file. Generated proxies and designer code put an
attribute on every member: 2,500 attributes in a 77 KB file advanced
38.5 M characters, and a 1.35 MB synthetic file took 21 s to parse.
The scan now fails at the first character well-formed XML cannot
contain: inside a tag, outside a quoted value, anything other than name
characters, blanks, '=', quotes, '/>', '>' and '<%='; in element text, a
'<' that starts no tag, closing tag, comment, PI or embedded expression,
and an '&' that starts no reference. No valid XML literal lexes
differently: parse trees are byte-identical before and after on all 25
sample and synthetic files tried, XML literals of every shape included.
The 1.35 MB file now parses in 0.08 s.
Residual, recorded in MANIFEST.md: an attribute without an argument list
(`<Fact>`) is a well-formed start tag, so its scan still runs until a
character that cannot be XML, or the end of the file.
The change is kept as tools/tree-sitter-vbnet/patches/
xml-literal-tag-syntax.patch (CodeGraph's scanner 8334fdb -> b3473d1)
and in MANIFEST.md's local-patch table. The new
complexity_vbnet_attribute_lexing_is_linear counts the bytes the lexer
pulls through a chunked TSInput for k and 2k attribute-decorated
classes: ratio 3.99 before the fix, 2.0 after.
Refs #1178, #1179
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Vendors govindbanura/tree-sitter-vbnet (MIT, (c) 2025 Govind Banura) at
538b7087bf80 (tag v0.1-beta, upstream HEAD) with CodeGraph's real-world
VB.NET patch (colbymchenry/codegraph docs/grammars/tree-sitter-vbnet.patch
at fe7542b6f6a4, MIT, (c) 2026 Colby Mchenry), as a self-maintained fork
in tools/tree-sitter-vbnet/. This is the precondition for VB.NET
extraction (#1179, folding in #2885); it registers no language yet.
Why this grammar: it is the licensed one. CodeAnt-AI/tree-sitter-vb-dotnet,
which #1179 was built on, carries no license and cannot be vendored.
Why the patch: upstream checks in grammar.js only and does not parse the
code VB.NET projects are made of. Unpatched, the Single literals in every
WinForms .Designer.vb (New SizeF(6.0!, 13.0!)) and body-less MustOverride
members are ERROR nodes. The 2026-10-06 revision of the patch is pinned
because it also keeps names that begin with a keyword whole: the earlier
revision (the one #2885 carried) turns `Sub NewItem()` into an ERROR and
`Public SharedCache As Integer` into a Shared field named Cache.
Provenance by hash: the patch file is byte-identical to CodeGraph's (blob
37918d81); upstream grammar.js at the pin is the patch's pre-image
(21c8523), and tools/tree-sitter-vbnet/grammar.js minus its 41-line
provenance header is the post-image (c9c1d85), as is scanner.c (8334fdb).
parser.c is generated here with tree-sitter-cli 0.25.10 (ABI 15); two
independent generations are byte-identical. No upstream script, package
hook or binary was run or vendored.
- internal/cbm/vendored/grammars/vbnet/: the generated parser.c,
CodeGraph's external scanner.c (two stateless tokens), the tree_sitter/
headers, upstream's MIT LICENSE byte-identical, and CodeGraph's MIT text
as LICENSE-codegraph: the scanner and the patched rules are CodeGraph's
work, so its notice has to ship with the binary too.
- internal/cbm/grammar_vbnet.c: compiles and links tree_sitter_vbnet. No
CBMLanguage, extension or alias routes to it yet; extraction adds them,
with `vb` and `visualbasic` meaning VB.NET (VB6 keeps its own name).
- MANIFEST.md (fork note, forks table, 163 grammars, ABI recount),
THIRD_PARTY.md (fork list and a vbnet section; the stale "160
pre-generated parsers" now reads 163), scripts/vendored-checksums.txt,
and the FORKS entry in scripts/audit-license-provenance.py.
- README.md and the Chocolatey nuspec say 163 languages: the
language-count contract defines the count as the vendored grammar
directories.
- tests/test_parse_coverage.c: real VB.NET (a WinForms Designer partial
class, Handles, Inherits/Implements, MustOverride, #Region, Property,
keyword-prefixed names) parses with no ERROR node. The case fails on
unpatched upstream (8 ERROR nodes) and on the earlier patch revision (1).
Refs #1178, #1179, #2885
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings the branch up to main (0d835c55). Two conflicts, both the
`drop_plain_call` expression in pass_calls.c and pass_parallel.c: main
appended the external-import-shadow and PHP receiver clauses where this
branch appended the #1128 same-module bound-receiver clause. Resolved as
main's clauses followed by `||` and the #1128 clause; clang-format then
wraps main's PHP line, which the added `||` pushed past the column limit.
No other change.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Strategy 2 (same_module) looks a member callee up a second time without
its receiver, so `axios.get(url)` in a file that defines its own `get`
became a 0.90 CALLS edge to that local function. The weak-member guard
deliberately keeps same_module, so this class was unguarded (#1128).
Restore the registry strategy chain to main (review of 2026-10-19,
item 1): the chain also resolves type references, field types,
reads/writes, SQL lineage and the cross-LSP pass for every language,
and the chain-level restriction broke the C typed-field and Python
external-import pipeline tests. Instead add a call-site suppressor,
wired identically in pass_calls.c and pass_parallel.c with the language
list inside the helper: for the JS/TS family and the <script> hosts it
drops the plain-CALLS fall-through only when the receiver-dropping
lookup produced the hit AND the receiver is bound to an ES import or a
CommonJS require of the file, to an AMD dependency, or is a builtin
global of the JS runtime (console, Math, JSON, Object, process, ...).
Every other receiver keeps its edge: object-literal methods and
functions assigned onto an object are stored at module level, so
`context.push()` reaching the module's `push` is usually right. A
whole-callee hit (`search.init()` -> <module>.search.init) keeps the
receiver in the match and is never refused. Service and route edges are
unaffected.
Measured with a fresh full index, sequential and parallel identical,
runs repeatable: vuejs/core same_module CALLS 1309 -> 1304, NodeBB
1721 -> 1680, no other strategy moves and no edge is added; every one
of the 49 removed call sites was read against the source and is a wrong
edge. helm (Go) is byte-identical.
The contributor's registry cases move to the guard as positive and
negative controls; pipeline tests cover JS, TS, TSX, ArkTS, Vue,
Svelte, Astro and HTML plus CommonJS and AMD, sequential and parallel,
and fail on main for every language.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The same-module guard for #1128 needs to know, from the caller's own
source, whether a member call's receiver is bound to another module.
Two facts were missing:
- A JS/TS import statement that binds no local name (side-effect
`import './x'`, a re-export, a bare or destructured `require('x')`,
`require('x').y`) still records the path's last segment as its
local_name, so `require('node:stream').Readable` made every `stream`
parameter look import-bound. CBMImport.local_name_synthesized marks
those stand-ins. A lazy `x = require('x')` and
`const x = require('x').member` do bind that very name and stay
unmarked. local_name itself is unchanged, so IMPORTS edges and the
import map are untouched.
- AMD `define([...], function (dep) {...})` / `require([...], fn)` bind
each factory parameter to the dependency at the same position. That
binding is local to the factory and is not an import (no IMPORTS
edge), so the unified walk carries it like the Python parameter
bindings: the loader call notes its factory, the factory's frame binds
the dependency parameters, the frame's pop unwinds them. The
CommonJS-wrapper pseudo dependencies (`exports`, `module`, `require`)
stay unbound. O(1) per node, no ancestor walk. The JS member-call
branch reads it into CBMCall.receiver_is_amd_dependency.
Nothing reads either fact yet; the graph is unchanged by this commit.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
A receiver's type came from every same-name binding of the enclosing
function, wherever in the function it was declared. When an inner
binding's type could not be inferred, the receiver counted as unknown
even where only an outer, declared binding was in scope, and the call
took the label match. In
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)
if flag {
let semaphore = identity(DispatchSemaphore(value: 0))
semaphore.signal()
}
semaphore.wait()
the outer wait() got an edge to an unrelated project wait() (#2436).
Each Swift binding now records where it is in scope: from the end of
its declaration to the end of its block, where a parameter's block is
its function. Only the bindings in scope at the call count, and of
those the innermost block's, so a local shadows a same-name local of an
enclosing block or a parameter. `var request = try request.make()`
reads the parameter on its right-hand side and the new variable after
it. Bindings of the same block that disagree still select nothing, and
fields and module-level bindings are unchanged.
pipeline_swift_in_scope_binding_issue2436 (sequential and parallel)
covers the example, the shadowed-parameter idiom of Alamofire's
encoders and two sibling blocks; it fails without the change.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
In a Cargo workspace a relative `use` bound a sibling crate's item. The
import resolver has no Rust branch, so `crate::util::helper` fell through
to the project-wide symbol-name fallback (Strategy 3), which matches the
last path segment, then every enclosing segment, against every same-named
definition in the project and keeps the lexicographically smallest QN:
whichever crate sorts first. The same fallback bound imports of external
crates to project symbols by name (`use serde_json::json` -> the project's
own `json` function, and every `json!` call through import_map) and let a
module segment bind a function (`crate::util::Gone` -> fn `util`), while
Strategy 4 resolved `crate::`, `self::` and `super::` from the repository
root instead of the crate's module root.
A Rust import of a file under a Cargo.toml is now classified by its first
path segment and resolved inside exactly one crate:
- `crate::` is the importing crate's module root: the nearest Cargo.toml's
src/ (or tests/, benches/, examples/; a build script is its own root);
- `self::` / `super::` are relative to the importing file's module path;
- a child module of the importing module is that module (2018 paths);
- a workspace member's crate name (a package `foo-bar` is `foo_bar`) is
that member;
- anything else (std, core, a dependency) binds no project symbol.
Inside the crate the item at the module the path names wins, else the
module the full path names, else a same-named item in a submodule of that
module (a re-export from below), else the type an associated item or enum
variant belongs to. Nothing else is guessed, ties break on module-path fit
then QN, and files with no Cargo.toml above them keep the generic
strategies. The CALLS import map is built from these IMPORTS edges in both
drivers, so import_map CALLS follow.
On jdx/mise this removes all 720 relative IMPORTS that crossed into
another workspace crate, all 1,123 IMPORTS bound through an external-crate
path and all 259 import_map CALLS that followed them; IMPORTS and
import_map CALLS into a crate the caller does not depend on drop from
920 and 12 to 0. Wall time and index worker peak RSS are unchanged; a
non-Rust control graph is byte-identical.
Strategy 4's module-prefix loop moves into resolve_module_prefixes so the
crate tree can reuse its lookup (plus the mod.rs form); Strategy 4's own
behaviour is unchanged.
Refs #2922, #2932
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The previous commit kept the label match for a constructor-typed receiver
only when the project declares the constructed type. After the census the
maintainers decided to recover every constructor-typed receiver like one
with an inferred initializer type: when the type has no member with the
call's name and labels, the call keeps the label match, whether or not
the project declares the type (NSLock(), DispatchSemaphore(), Data()).
For constructors of types the project does not declare, that label match
is often an unrelated project method (a semaphore's wait() lands on
EventLoopFuture.wait on swift-nio). It is the same label match a receiver
of unknown type gets; tightening it for receiver chains that pass through
a property is a separate follow-up.
Annotations and parameters still decide alone, and inferring an
initializer's type still never uses a label match. The NSLock and Data
rows of pipeline_swift_constructor_receiver_keeps_label_match_issue2061
now expect the label-matched edge.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings the branch up to date with main (e2a93e4ca): #2205 (daemon UI
setting on manual start), #2905 (Retrofit client HTTP_CALLS) and two
GitHub Actions version bumps. No conflicts.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>