Repository navigation
fix(incremental): keep C# extension methods of unchanged files - #3295
Conversation
`b.Describe()` binds to `static string Describe(this Box b)` through `csharp_extension_methods`, which only a parse of the declaring class fills. An incremental run that re-parsed the caller alone found no extension and dropped the call (serilog: 21 edges into `Extensions.LiteralValue`). Each extension method's index entry -- receiver type, declaring namespace, receiver generic arity -- is now stored on its Method node at ingest and added back to the index when the run rehydrates a definition it did not parse. Fixes #3275 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EbsCWQcSTGQXv3g5sk418m
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (9)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 2 remain after this review. 📝 WalkthroughWalkthroughThe change stores C# extension-method receiver type, declaring namespace, and receiver generic arity as method properties. Definition queries return these values, and incremental rehydration uses them to restore the C# extension-method index. Regression tests compare incremental indexing with fresh indexing. ChangesC# Extension Method Rehydration
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium Sequence Diagram(s)sequenceDiagram
participant IncrementalSync
participant DefinitionQuery
participant GraphUpdater
participant CSharpExtensionIndex
IncrementalSync->>DefinitionQuery: Fetch definition rows with extension metadata
DefinitionQuery->>GraphUpdater: Return receiver, namespace, and arity
GraphUpdater->>CSharpExtensionIndex: Restore extension-method entry
Suggested reviewers: Merge Risk: ⚪ Minimal · up to An incremental sync now keeps C# extension-method call edges for files that were not reparsed. No merge-blocking risk was identified in the supplied changes. Pre-merge checks |
|
|
|
Heads-up: this says
The repros are in the #3275 comments. Using Generated by Claude Code |
|



Summary
b.Describe()withstatic string Describe(this Box b)andservices.AddMyServices(). A fresh index binds themexact; after a comment-only edit to the caller there was no edge at all. On serilog/serilog, 21 CALLS edges intoExtensions.LiteralValueexisted only in a fresh graph.recv.Ext()is resolved by_find_extension_methodfromcsharp_extension_methods, aname → [(qn, receiver type, namespace, arity)]index filled only while the declaring class is parsed._rehydrate_registry_from_graphrestored the method into the registry but never into that index, and the receiver type and declaring namespace were not on the node to restore it from.index_extension_methodis split intoextension_method_facts, which computes the entry from the declaration, andadd_extension_method, which adds it to the index. Both the parse and the rehydration use the latter, so the two produce the same tuples.ingest_methodstores the facts on C# extension Method nodes:extension_receiver,extension_namespacewhen not empty, andextension_receiver_aritywhen not zero. That covers the class-member pass and the#if-truncation recovery.CYPHER_ALL_DEFINITION_QNSreturns the three properties, and_rehydrate_definition_row, which only reaches definitions the run did not parse, adds the entry back.graph-schema.mdregenerated), declared unexported in the protobuf parity list with their reason, and returned by the eval graph double.Demo
The GIF plays the issue's
csextrepro step by step against Memgraph.BoxExtensions.csdeclaresDescribe(this Box b)andAddMyServices(this Services s), andProgram.Runcalls both.callees Program.Run, a comment-only edit toProgram.cscommitted, an incrementalcgr start --update-graph, andcalleesagain.BoxExtensions.AddMyServices (exact)andBoxExtensions.Describe (exact), then[]after the sync.exactcallees before and after the sync. The new tests pass.Type of Change
Related Issues
Fixes #3275
Test Plan
make test): related suites run, full suite see belowRed/green TDD in
codebase_rag/tests/test_incremental_csharp_extension_methods.py: a fresh index and then an incremental run, through the stateful graph double. The fixture adds generic twinsBuilder/Builder<T>, each with its ownBuildextension.test_a_reparsed_caller_keeps_its_extension_calls:Describe,AddMyServicesand the generic twin'sBuildmust keep theirexactedges after an edit toProgram.csalone. On main all three vanish.test_the_rehydrated_index_is_the_one_a_parse_builds: a fresh process's rehydratedcsharp_extension_methodsmust equal the index a full parse builds, field for field (receiver type, namespace, arity).test_an_edited_extension_binds_as_a_fresh_index_does. WithBoxExtensions.csalso re-parsed (Describenow extendsServices), the incremental run binds exactly what a fresh index of the same tree binds, and never the storedDescribe(Box).Builder`1).test_csharp*,test_cs_*,test_incremental*,test_reingest*,test_graph_updater_phase*,test_protobuf*,test_eval_double*,test_generated_docs*andtest_rehydrat*file: 647 passed, 72 skipped._test.go): after an incremental re-parse,b, _ := NewBox(2); b.Size()bindsheuristicto another type'sBag.Sizeinstead ofexactBox.Size:go_function_return_typesof unchanged files is never rehydrated (gin: 279 context_test call keys flip after a commit touching only response_writer.go) #3272) also adds Method properties and columns toCYPHER_ALL_DEFINITION_QNS; whichever merges second keeps both.-n 4): 109 failed, 17,709 passed, 177 skipped. The 109 failures are the same set main fails in this environment (LLM provider keys, the build-binary check, the unreachable-network tests); none is new.Checklist
StrEnum)# type: ignore,cast(),Any, orobjecttype hints🤖 Generated with Claude Code
https://claude.ai/code/session_01EbsCWQcSTGQXv3g5sk418m
Generated by Claude Code