Skip to content

fix(registry): recognize C# lower-case type keywords in receiver_chain_admits - #2170

Merged
DeusData merged 9 commits into
DeusData:mainfrom
AmirF194:fix/2121-csharp-lowercase-receiver-type-keyword
Nov 11, 2026
Merged

DeusData merged 9 commits into
DeusData:mainfrom
AmirF194:fix/2121-csharp-lowercase-receiver-type-keyword

Conversation

@AmirF194

@AmirF194 AmirF194 commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

receiver_chain_admits() (src/pipeline/registry.c) fast-admits a lower-case-rooted receiver chain as a value rather than a type. That's correct for Go, Python and Swift (vm.load, http.Get, os.path.join), but C# spells its built-in types as lower-case keywords (int, string, bool, ...), so a static call like int.TryParse(...) skips the chain-consistency check entirely and can fabricate a CALLS edge to any unrelated method sharing the same name (#2121).

Fix: a closed set of C# type keywords is checked only when the receiver root is lower-case; anything not in that set still fast-admits exactly as before, so other languages' lower-case value roots are unaffected.

Refs #2121

What does this PR do NOT fix?

The issue's second example, Options.Create(new object()), no longer reproduces at HEAD: PR #1897 (merged 2026-09-04) already added the chain-consistency guard this PR extends, and it happens to cover that upper-case-rooted shape already.

How was this verified?

  • Added registry_receiver_chain_refuses_lowercase_type_keyword_issue2121. Without the fix, ASSERT_NULL in that test trips because the fabricated edge still resolves; with the fix it's clean. Ran both ways in the same Docker image (ubuntu:24.04, ASan+UBSan test build).
  • Reproduced the issue's own two-file C# example end to end with the built CLI (index_repository + trace_path): int.TryParse fabricates an edge to an unrelated TryParse on main; zero edges on this branch.
  • Added a second test, registry_receiver_chain_keeps_type_keyword_when_chain_matches_issue2121, confirming the guard still resolves a real project type whose chain matches, so it isn't just refusing everything lower-case.
  • Full pipeline suite (279 tests) green on this branch; cppcheck and clang-format --dry-run clean on the changed file. Not checked against the exact CI toolchain: local cppcheck was 2.13.0 and clang-format 18.1.3, CI pins 2.20.0 and clang-format-20.

…n_admits

receiver_chain_admits() fast-admits any lower-case-rooted receiver chain
as a value, not a type, which is true in Go/Python/Swift but not for
C#'s built-in type aliases (int, string, bool, ...): those are
lower-case KEYWORDS that name a type, so a static call like
int.TryParse(...) skips the chain-consistency check and can fabricate a
CALLS edge to any unrelated same-named method (DeusData#2121).

Add a closed keyword set consulted only for a lower-case root, so every
other language's lower-case value roots (vm.load, http.Get,
os.path.join) are unaffected.

Signed-off-by: Amir Fathi <[email protected]>
@AmirF194
AmirF194 requested a review from DeusData as a code owner September 10, 2026 20:04
@github-actions

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

@AmirF194

Copy link
Copy Markdown
Contributor Author

Both red legs here (test-windows CLANG64 timing race, and the downstream shard-completeness failure that depends on it) are outside this diff, neither touches registry.c or test_pipeline.c. Been open a week with no look yet, let me know if anything's blocking review.

@DeusData DeusData added bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. labels Sep 19, 2026
@DeusData

Copy link
Copy Markdown
Owner

Thank you for the C# keyword fix and negative controls. The current registry's lower-case receiver handling supports the reported gap. Review should preserve the C# keyword cases without admitting unrelated receiver names in other languages. We still need to attribute the Windows failure from its evidence; an unchanged Windows-specific file list alone would not prove it unrelated. The contribution is queued for that focused review.

@AmirF194

Copy link
Copy Markdown
Contributor Author

Read the job log rather than the file list this time. The windows-latest CLANG64 leg fails inside tests/test_parallel_harness_contract.sh's own scheduler self-test, step 0i: "FAIL: Windows timeout race refused without a surviving descendant to refuse over, the fixture no longer exercises the race" (job 103034332390). That's a timing fixture for the test harness's own process cleanup, nothing in registry.c or test_pipeline.c. shard-completeness and ci-ok both fail downstream of that missing shard manifest (job 103051948933 shows no manifest uploaded for windows-latest-CLANG64-0).

@DeusData DeusData left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this. The C# bug is real and your fix for it is right: in int.TryParse, string.Join or object.ReferenceEquals the lower-case root is a type, and treating a reserved type keyword the way an upper-case root is treated fixes all of them cleanly. I verified it on the merge result with today's main (e783f73d): clean merge, even though both sides edited tests/test_pipeline.c; pipeline + registry 357 passed, including both issue2121 tests; memory-core linter unchanged.

One change before it merges, and it is a rule of the codebase rather than a nit. Your comment says the keyword list is deliberately language-agnostic ("closed list, no caller-language read"). Our standing rule for call resolution is the opposite: suppressors stay per-language and are gated on the file's language, so a guard that fixes one language can never trade away recall in another. Every existing guard in registry.c follows it for exactly that reason. Here the words are reserved type keywords only in C#. In Python, JavaScript or Go, object, string, char, float and dynamic are ordinary identifiers, so a lower-case variable named object calling a method would now fall through to the chain-consistency check instead of passing as a value root, and that can drop a real CALLS edge in a language this PR never meant to touch.

What I would ask for:

  1. Apply the type-keyword branch only when the calling file is C#. The registry already has the shape for this: the per-language gate is decided at the call sites and passed into the helper as enabled, never read inside it — see the comment above the Python / JS-TS guard. Those call sites are in pass_calls.c and pass_parallel.c, and they must enumerate the identical language set, or the sequential and parallel resolvers diverge. Please keep that lockstep.
  2. Keep your two C# tests, and add the negative control that pins the other side: a Python (or Go) file in which a lower-case variable named object (or string) calls a method, asserting that the CALLS edge still resolves. That test is what will stop the next widening of the list from quietly costing another language.

If another language turns out to need the same treatment later, it can join the gate on its own, with its own test. Thank you for the careful true-positive test you already wrote; it is the reason this is a small ask rather than a redesign.

The DeusData#2121 fix treated C#'s reserved type keywords (int, string, object,
...) as value roots in receiver_chain_admits without checking the
calling file's language. In Python, JS or Go those same spellings are
ordinary identifiers, so a lower-case variable named object or string
calling a method would fall through to the chain-consistency check and
could drop a real CALLS edge outside C#.

Add cbm_registry_resolve_lang(..., is_csharp) alongside the existing
language-blind cbm_registry_resolve, and thread is_csharp from each
file's own language at every call site that resolves a real receiver
chain: pass_calls.c and pass_parallel.c (the sequential/parallel CALLS
resolvers named in review), plus pass_semantic.c and pass_usages.c so
INHERITS/DECORATES/THROWS/RW edges stay gated the same way rather than
silently reverting to the pre-DeusData#2121 language-blind behavior. The two
remaining language-blind callers (pass_calls.c's Python-only Depends
scan, pass_lsp_cross.c's base-QN resolve which already excludes C#)
are documented in place as never reaching a C# receiver chain.

Adds a negative control test asserting a lower-case object/string
receiver root still resolves as a value in a non-C# file, pinning the
side of the gate a widened keyword list could quietly cost.

Signed-off-by: Amir Fathi <[email protected]>

@AmirF194 AmirF194 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both applied. cbm_registry_resolve_lang(..., is_csharp) sits alongside the language-blind cbm_registry_resolve, and is_csharp is threaded from each file's own language at every real receiver-chain call site: pass_calls.c and pass_parallel.c as you named, plus pass_semantic.c and pass_usages.c so INHERITS/DECORATES/THROWS/RW stay gated the same way instead of quietly reverting to language-blind. Two remaining language-blind callers (pass_calls.c's Python-only Depends scan, pass_lsp_cross.c's base-QN resolve which already excludes C#) are documented in place as never reaching a C# receiver chain.

Added the negative control: a lower-case object/string receiver root in a non-C# file still resolves as a value, pinning the side a widened keyword list could quietly cost.

Verified in a clean container: 289/290 pipeline tests pass (the one failure, git_context_linked_worktree, needs a git binary the container doesn't have and is unrelated to this change). Pushed 5a3e825.

Column-limit re-wrap only, no token changes. Reproduced the CI lint
failure with clang-format 18/20 (BasedOnStyle: LLVM, ColumnLimit: 100)
against the .clang-format the repo's own lint.sh uses.

Signed-off-by: Amir Fathi <[email protected]>
@AmirF194

Copy link
Copy Markdown
Contributor Author

Fixed and pushed (1ebd1e4). The lint job's clang-format check was failing on the three new cbm_registry_resolve_lang call sites, all over the 100-column limit. Reproduced with clang-format 18 and 20 against the repo's own .clang-format, re-wrapped, confirmed clean with a dry-run diff. Whitespace only, no token changes.

@DeusData DeusData left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @AmirF194, for the fix and for turning around the last review so quickly. The keyword gate is the right idea: in int.TryParse, string.Join or object.ReferenceEquals the lower-case root is a type, and your three tests pin both sides of that. I'm sorry this sat long enough for main to move underneath it. That is on us, not you.

Since your last push, main gained a language-aware registry API of its own (132ded8, 3 October): cbm_registry_resolve_lang(..., CBMLanguage lang) (declared in src/pipeline/pipeline_internal.h, defined in src/pipeline/registry.c) and cbm_registry_resolve_call_lang(..., CBMLanguage lang). The branch now conflicts with main, and the two cbm_registry_resolve_lang declarations clash. So here is what we would ask for:

  1. Rebase onto current main.
  2. Derive the C# gate from the CBMLanguage that main's resolve functions already receive, instead of adding a bool is_csharp parameter. That should make most of the call-site threading unnecessary, and keeps one registry API. The reason this matters beyond tidiness: if a rebase kept main's CBMLanguage parameter and the call sites still passed lang == CBM_LANG_CSHARP, C would accept it silently. CBM_LANG_GO is 0 and CBM_LANG_PYTHON is 1, so C# files would resolve with Python scope and every other file with Go scope.
  3. Keep your three tests: the int.TryParse refusal, the chain-matches positive control, and the non-C# object./string. value-root control.

On scope: please keep this PR to the CALLS resolvers (pass_calls.c and pass_parallel.c), as the 23 September review asked. The extension to C# USAGE, THROWS/RAISES, READS/WRITES, INHERITS, DECORATES and route-handler lookups in 5a3e825 is very welcome, and is probably a quality gain too. As a separate follow-up PR, we can measure it on its own, so a reviewer sees exactly what each change moves. Our house rule is one claim per PR, so the two get judged separately rather than one holding up the other.

Once the rebased version is up we will measure it on our C# corpora, together with a related change on our side that uses the C# LSP's own answer for BCL receivers (int.TryParse already resolves to System.Int32.TryParse there). Your keyword gate still matters for receivers the LSP says nothing about. Thanks again for sticking with this!

@DeusData DeusData left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for this, @AmirF194! The analysis in the description is spot on: receiver_chain_admits fast-admits every lower-case receiver root as a value, and C# spells its built-in types as lower-case keywords. So int.TryParse(...) skipped the chain check entirely and could bind to any project method named TryParse. That is exactly #2121. The closed, C#-only keyword list is the right shape: these words are grammar, not a name heuristic, and gating it on the file's language keeps object, string and friends ordinary identifiers in Python, JS and Go. The second test, showing that a real project type whose chain matches still resolves, is a great touch.

Main has moved quite a bit under this branch since September, so a few requests before it can land:

  1. Name clash with main. Main now has cbm_registry_resolve_lang(..., CBMLanguage lang) in src/pipeline/pipeline_internal.h, the same name as this PR's new cbm_registry_resolve_lang(..., bool is_csharp), so the branch will no longer build once main is merged in. It also means the language is already available where you need it:

    • Both resolvers now reach the registry through one language-aware entry point, cbm_registry_resolve_call_scoped(..., CBMLanguage lang, ...) → cbm_registry_resolve_call_lang(...) → registry_resolve_scoped(...), used by pass_calls.c and pass_parallel.c alike.
    • Could you pass that CBMLanguage down to resolve_name_lookup → receiver_chain_admits(callee, candidate, lang) and test lang == CBM_LANG_CSHARP there, instead of threading a new bool is_csharp through the call sites?
    • That would shrink the diff to registry.c plus the tests. It also removes the seq/parallel lockstep risk the comments warn about, because there is only one entry point to keep in sync.
  2. Keyword list: consider adding nint and nuint (C# 9 native-sized integers). They are keywords in receiver position too (nint.TryParse).

  3. An end-to-end test on both resolvers would make the fix stick. tests/test_parallel.c has a harness that indexes a small fixture sequentially and in parallel (run_sequential_with_lsp_cross / run_parallel). The issue's own two files fit it well:

    • int.TryParse(input, out _) must not create CALLS to the project's TsidNode.TryParse;
    • control: TsidNode.TryParse(input, out _) still binds.
  4. Lint: CI's lint is scripts/lint.sh --ci. Besides cppcheck 2.20 and clang-format 20 (the Homebrew LLVM build; a standalone clang-format-20 can report false whole-file drift), it runs:

    • the NOLINT whitelist check;
    • the memory-core ratchet, under which a file's raw malloc/calloc/realloc/free/strdup count must not grow. Allocate through src/foundation/mem_core.h. A new table or list is a common way to trip it.

    A run of that script after the main merge would catch all three.

One heads-up so our work fits together. We are about to open a PR for #1692: log calls inside Kafka consumers became fake Kafka routes. There, the C# LSP already proves that the receiver is a library type, for example m_Logger.LogError → Microsoft.Extensions.Logging.ILogger.LogError, but that target has no graph node, so the pipeline fell back to a name guess. The fix stops the name guess when the LSP resolved the call to a library symbol, as main already does for Rust (#2053).

  • Because the C# LSP also knows int.TryParse is System.Int32.TryParse, that change will stop the #2121 fabrication too, whenever the LSP resolves the receiver.
  • Your registry-side check stays valuable: it covers the calls the LSP does not resolve, and it keeps the name-guess path honest on its own. So the two complement each other, and we will reference this PR in ours.

Thanks again for the careful work and the clear write-up!

DeusData and others added 2 commits October 20, 2026 19:29
Brings the branch up to date with main a222715. Since September, main
added its own language-aware registry API (132ded8):
cbm_registry_resolve_lang(..., CBMLanguage lang), under the same name as
this branch's cbm_registry_resolve_lang(..., bool is_csharp), and both
CALLS resolvers now reach the registry through one entry point,
cbm_registry_resolve_call_scoped -> cbm_registry_resolve_call_lang.

Seven files conflict: pass_calls.c, pass_lsp_cross.c, pass_parallel.c,
pass_semantic.c, pass_usages.c, pipeline.h and registry.c. All seven are
resolved to main's side, since main's CBMLanguage parameter supersedes
the is_csharp threading. The next commit re-applies the C# keyword gate
on main's API. tests/test_pipeline.c merged cleanly.

Signed-off-by: Martin Vogel <[email protected]>
Follow-up to the review of DeusData#2170, on top of the contributor's change.

receiver_chain_admits() fast-admits a lower-case receiver root as a
value. C# spells its built-in types as lower-case keywords, so
int.TryParse skipped the chain check and could bind to any project
method named TryParse (DeusData#2121). This keeps the contributor's closed
keyword list and moves the gate onto the language main already passes:

- cbm_registry_resolve_call_lang, the CALLS entry point both resolvers
  share through cbm_registry_resolve_call_scoped, hands the calling
  file's CBMLanguage down to receiver_chain_admits, which applies the
  keyword branch only for CBM_LANG_CSHARP. Every other resolve passes
  CBM_LANG_COUNT, so USAGE, THROWS/RAISES, READS/WRITES, INHERITS,
  DECORATES and route-handler lookups are unchanged; extending the gate
  to them is a separate change.
- nint and nuint (C# 9 native-sized integers) join the list.
- The per-file resolve cache records the C# bit, so a C# call and a
  C# reference to the same name never share one cached answer.
- The three unit tests call the CALLS entry point with a real
  CBMLanguage (C#, Python, Go) instead of a bool.
- parallel_csharp_keyword_receiver_no_name_guess_issue2121 indexes the
  issue's two files with the sequential and the parallel pipeline: no
  CALLS edge from int.TryParse or nint.TryParse to the project's
  TsidNode.TryParse, while TsidNode.TryParse(...) still binds.

Co-authored-by: Amir Fathi <[email protected]>
Signed-off-by: Martin Vogel <[email protected]>
@DeusData

Copy link
Copy Markdown
Owner

Thank you, @AmirF194, for this fix and for your patience while main moved under it. Your diagnosis was exactly right: receiver_chain_admits let every lower-case receiver root through as a value, and C# spells its built-in types as lower-case keywords, so int.TryParse(...) skipped the chain check and could bind to any project method named TryParse. The closed, C#-only keyword list is the right shape, and your three tests (the refusal, the matching-chain true positive and the non-C# control) carried straight over. The open review points were already settled and the branch allows maintainer edits, so we finished them here instead of asking you for another round.

What we changed, on top of your commits (which are untouched):

  1. Merged current main in (one merge commit). main had meanwhile added its own cbm_registry_resolve_lang(..., CBMLanguage lang) under the same name as this branch's bool is_csharp version, and both CALLS resolvers now reach the registry through one entry point, cbm_registry_resolve_call_scoped. Seven files conflicted. We resolved all seven to main's side, because main's language parameter replaces the is_csharp threading.
  2. The keyword gate now uses main's CBMLanguage. cbm_registry_resolve_call_lang passes the calling file's language down to receiver_chain_admits, which applies your keyword list only for CBM_LANG_CSHARP. Both resolvers share that one entry point, so the sequential and parallel paths cannot drift apart, and the production change is confined to registry.c (plus one comment in pipeline_internal.h). Every non-CALLS resolve passes no language, so USAGE, THROWS/RAISES, READS/WRITES, INHERITS, DECORATES and route-handler lookups behave exactly as before, as the earlier review asked. Extending the gate to them is welcome as its own PR.
  3. nint and nuint joined the list (C# 9 native-sized integers, as in nint.TryParse).
  4. The per-file resolve cache records the C# bit, so a C# call and a C# reference to the same name never share one cached answer.
  5. Your three unit tests call the CALLS entry point with a real language (C#, Python and Go) instead of a bool, and the refusal test also covers nint.TryParse.
  6. A new end-to-end test, parallel_csharp_keyword_receiver_no_name_guess_issue2121, indexes the two files from BCL/external calls fabricate CALLS edges to unrelated internal classes (phantom callers; e.g. int.TryParse → internal TryParse) #2121 with both the sequential and the parallel pipeline: no CALLS edge from int.TryParse or nint.TryParse to TsidNode.TryParse, while TsidNode.TryParse(...) still binds.

How we checked it: the new end-to-end test and your refusal test fail on main (the fabricated edge shows up in both pipelines) and pass with the change. The pipeline, registry, parallel, cs_lsp, convergence_probe and matrix_new_constructs suites pass. cppcheck 2.20, clang-format 20, the NOLINT check and the memory-core ratchet are clean. The issue's own two files no longer produce the Demo.M -> TsidNode.TryParse edge. On four libraries from dotnet/runtime (about 1,700 C# files), the change removes 68 fabricated CALLS edges and adds none. Examples are string.Format bound to a project's DecimalFormatter.Format and char.IsWhiteSpace bound to XmlCharType.IsWhiteSpace. The sequential and parallel results are identical.

One limit to be aware of: when several project methods share the name and the file has using imports, the registry takes a different path that never consults the receiver chain, in any language. Fixing that is a separate, wider change, so it stays out of this PR.

We will merge once CI is green on the new head. Thank you again for the careful work and the clear write-up!

@DeusData

Copy link
Copy Markdown
Owner

Thank you again for the C# keyword guard and controls. The October 20 update records the language-parameter adaptation and end-to-end coverage as completed on our side. Sorry for the delay in the remaining maintainer follow-up—I’ll follow up soon.

`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 <[email protected]>
Brings the branch up to date with main 6d34ca1. 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 (DeusData#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 <[email protected]>
@DeusData

Copy link
Copy Markdown
Owner

Thank you again, @AmirF194, and sorry this took longer than the "soon" we promised on October 25. Your fix has held up well: on current main, int.TryParse(...) in a C# file can still bind to any project method named TryParse, and your keyword gate is still what stops it.

We pushed two more commits on top of your branch (your commits are untouched):

  1. dynamic left the keyword list. int, string, object and the other reserved type keywords can only ever name the built-in type, and nint and nuint name it in practice too. dynamic is different: it is a contextual keyword, and the dynamic type has no static members, so in receiver position it is a variable that happens to be called dynamic. Such a receiver now follows the ordinary value-root rule again, like any other lower-case variable. We checked this on dotnet/runtime: all 586 nint. and nuint. member accesses there are static members of the built-in types (Size, MaxValue, Parse, TryParse, ...), while none of the 10 dynamic. receivers is the built-in type: nine are variables named dynamic, and one is a test enum declared as @dynamic. A new C# test pins it: a call through a variable named dynamic still binds.
  2. Merged current main in (one merge commit). Two spots needed care: main added two C# tests to tests/test_parallel.c at the same place as this branch's end-to-end BCL/external calls fabricate CALLS edges to unrelated internal classes (phantom callers; e.g. int.TryParse → internal TryParse) #2121 test, and all three are kept; and main gained a new Swift caller of receiver_chain_admits, which now passes its language like the other callers. Swift behaves exactly as before, since the keyword list applies to C# only.

How we checked it: the new dynamic test fails with dynamic still on the list and passes without it, and your refusal, matching-chain and non-C# control tests still pass. After the merge, the pipeline, parallel, registry and cs_lsp suites pass under AddressSanitizer and UndefinedBehaviorSanitizer, including the Swift tests that go through the adapted caller. The two files from #2121 still lose the Demo.M -> TsidNode.TryParse edge, while TsidNode.TryParse(...) still binds. On four dotnet/runtime libraries (System.Private.Xml, System.Data.Common, System.Formats.Asn1 and System.Runtime.Numerics, 1,611 C# files), the branch removes 26 CALLS edges compared with main and adds none, and the sequential and parallel pipelines build identical results. We read all 26: each one was a static call on a built-in type that had been bound by name to an unrelated project method, for example char.IsAsciiDigit to XPathScanner.IsAsciiDigit, decimal.GetBits to DataStorage.GetBits and double.IsNaN to BigInteger.IsNaN.

One correction to our October 19 note: the C# LSP change for #1692 that we said would complement this PR is not going ahead for now, so your registry-side check is what fixes this part of #2121.

If you push to this branch again, please pull first, since it now has our commits on top. We will merge once CI is green. Thank you for the careful work and the clear write-up!

@DeusData
DeusData merged commit f61d7c0 into DeusData:main Nov 11, 2026
26 checks passed
DeusData added a commit to ankitsusheel/codebase-memory-mcp that referenced this pull request Nov 12, 2026
Brings the branch up to main ff812bf.

Conflict in src/pipeline/registry.c: main (DeusData#2170) gave
registry_resolve_scoped a `call_lang` argument (the calling file's
language on the CALLS path, CBM_LANG_COUNT on every other lookup),
while this branch wrapped both lookups with the VB.NET case-insensitive
retry. Resolved by keeping the retry and passing main's argument
through: vb_resolve_folded takes `call_lang` and hands it to its own
registry_resolve_scoped call, CBM_LANG_COUNT from
cbm_registry_resolve_lang and the caller's language from
cbm_registry_resolve_call_lang, as main does for the first lookup.

Signed-off-by: Martin Vogel <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants