Skip to content

fix(csharp): extract delegate declarations so references don't mint sourceless stubs - #4556

Open
rajatnagda45 wants to merge 1 commit into
Graphify-Labs:v8from
rajatnagda45:fix/csharp-delegate-declarations
Open

rajatnagda45 wants to merge 1 commit into
Graphify-Labs:v8from
rajatnagda45:fix/csharp-delegate-declarations

Conversation

@rajatnagda45

Copy link
Copy Markdown
Contributor

Closes #4552.

Problem

A C# delegate is a named type, but delegate_declaration was in neither class_types nor function_types, so the declaration was dropped from the graph. A field or parameter typed as the delegate (public BinOp Op;, int Apply(BinOp f, ...)) still resolved the name BinOp through ensure_named_node, which minted a sourceless stub and pointed the references edge at it — even though BinOp is declared in the same file.

That violates the provenance invariant: a real source-backed declaration must not lose to a sourceless god node (and the bare-id stub becomes a cross-file hub shared by every same-named reference in the corpus).

Implementation

delegate_declaration has a name field and no body, so adding it to the C# class_types emits a clean empty type node (no phantom members) that the in-file references now bind to. Namespaced delegates are scoped and contained exactly like the sibling class/enum/struct declarations.

Verification

  • New regressions in tests/test_csharp_delegate_declarations.py (non-namespaced + namespaced: delegate is a sourced node, references bind to it, no sourceless leftover). Both fail on pre-fix v8, pass after.
  • Commands run:
    • python -m pytest tests/ -q --ignore=tests/test_security.py → 8331 passed, 15 skipped (deselecting the live-DNS security tests and the built-wheel test).
    • python -m pytest tests/ -k csharp → 312 passed (no regressions, incl. the delegate-field/enum-collision test).
    • ruff check . → clean.
    • pyright graphify/extract.py → unchanged from baseline (0 added).

Limitations

Delegate parameter/return type references and delegate-instance invocation resolution are unchanged (out of scope).

…ourceless stubs

A C# `delegate` is a named type, but `delegate_declaration` was in neither
`class_types` nor `function_types`, so the declaration was dropped from the
graph. A field or parameter typed as the delegate (`public BinOp Op;`,
`int Apply(BinOp f, ...)`) still resolved the name `BinOp` through
`ensure_named_node`, which minted a *sourceless* stub and pointed the
`references` edge at it — even though `BinOp` is declared in the same file.

That violates the provenance invariant: a real source-backed declaration must
not lose to a sourceless god node (and the bare-id stub becomes a cross-file
hub shared by every same-named reference in the corpus).

`delegate_declaration` has a `name` field and no body, so adding it to the C#
`class_types` emits a clean empty type node (no phantom members) that the
in-file references now bind to. Namespaced delegates are scoped and contained
exactly like the sibling class/enum/struct declarations.

Delegate parameter/return type references and delegate-instance invocation
resolution are unchanged (out of scope).
@github-actions

Copy link
Copy Markdown

Thanks for the pull request, @rajatnagda45. A maintainer will review it soon.

Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions.

A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic.

@github-actions github-actions Bot added the contributor Pull request from a returning community contributor label Oct 18, 2026

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.

Not checked: tests were not run; no formal proof of the changed code.


Graphify review — findings

Extracts C# delegate declarations as named type nodes by adding delegate_declaration to the C# class_types. Before, the declaration was dropped, so in-file references to the delegate (e.g. a BinOp field or parameter) went through ensure_named_node and minted a sourceless stub node. Those references now bind to a real, source-backed node, and the new tests cover both top-level and namespaced delegates.

No blocking issues surfaced.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 3115 functions depend on the 330 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: extract() — 976 callers, 59 callees

Verification — 3115 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 2877 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

199 of 433 test file(s) selected (46%) via static blast radius.

  • tests/test_affected_overrides.py — impact
  • tests/test_astro_extraction.py — impact
  • tests/test_astro_import_ids.py — impact
  • tests/test_blade_extractor.py — impact
  • tests/test_build.py — impact
  • tests/test_builtin_global_type_refs.py — impact
  • tests/test_c_include_resolution.py — impact
  • tests/test_cache.py — impact
  • tests/test_case_sensitive_resolution.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_cobol_extractor.py — impact
  • tests/test_cpp_method_declarations.py — impact
  • tests/test_cpp_nested_and_cli.py — impact
  • tests/test_cpp_objc_cross_file_calls.py — impact
  • tests/test_cpp_template_type_params.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_cross_language_call_resolution.py — impact
  • tests/test_cross_repo_external_call_guards.py — impact
  • tests/test_cross_repo_member_calls.py — impact
  • tests/test_csharp_call_site_generic_args.py — impact
  • tests/test_csharp_delegate_declarations.py — impact, changed-test
  • tests/test_csharp_enum_members.py — impact
  • tests/test_csharp_field_generic_args.py — impact
  • tests/test_csharp_foreach_loop_variable.py — impact
  • tests/test_csharp_generic_callsites.py — impact
  • tests/test_csharp_interface_dispatch.py — impact
  • tests/test_csharp_member_calls.py — impact
  • tests/test_csharp_member_chain_calls.py — impact
  • tests/test_csharp_member_nodes.py — impact
  • tests/test_csharp_object_creation.py — impact
  • tests/test_csharp_partial_classes.py — impact
  • tests/test_csharp_tuple_type_refs.py — impact
  • tests/test_csharp_type_resolution.py — impact
  • tests/test_csharp_underscore_collisions.py — impact
  • tests/test_csharp_var_call_return_type.py — impact
  • tests/test_csharp_verbatim_interpolation.py — impact
  • tests/test_definition_file_portability.py — impact
  • tests/test_detect.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_duplicate_annotation_edges.py — impact
  • tests/test_ejs_extractor.py — impact
  • tests/test_elixir_defdelegate.py — impact
  • tests/test_elixir_import_resolution.py — impact
  • tests/test_elixir_keyword_def_calls.py — impact
  • tests/test_elixir_qualified_calls.py — impact
  • tests/test_elixir_unqualified_call_scope.py — impact
  • tests/test_erlang_extractor.py — impact
  • tests/test_ext_tagged_id_clash.py — impact
  • tests/test_extract.py — impact
  • tests/test_extract_cache_location.py — impact
  • … and 149 more

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

· 1 more finding(s) on lines outside this diff (see the check run).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor Pull request from a returning community contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C# delegate declarations dropped, so in-file references mint a sourceless god node

1 participant