query: find_usages serves value-side reads/writes of variables and fields - #671
Conversation
…elds The C# field-identifier emitter lands resolved reads/writes edges on field nodes, and the resolver binds them to the enclosing type's own field - but isUsageEdgeKind filtered both kinds out, so find_usages on a field still answered empty while the store held the authored story (one write, N reads). Found by re-probing the original field false-empty case on a freshly rebuilt store: the reads edge for a call-receiver use inside a constructor-initializer lambda was present and resolved, and the usages view served zero rows. reads/writes are the value-side usage story for variables and fields by edge.go's own definition. accesses_field stays excluded: it is the synthesized union of the two riding the same sites, and including it would double-count every one. The engine test pins a field with one write, one read, and both accesses_field siblings (exactly two usage rows, no duplicates). Two extractor tests pin the positions that motivated the re-probe, all already emitting correctly: field reads inside a ctor-initializer block lambda and behind an expression-lambda argument, and writes from expression-bodied and static constructors plus reads in a C# 12 primary-constructor class.
@pbednarcik, in #668 you've added changes to the parser, but the parser version wasn't bumped, which may contribute to this (haven't checked it explicitly; worth validating it) |
|
Confirmed, good catch. #668 added a new edge kind without bumping extractorVersions, so a store upgraded in place never re-extracts unchanged .cs files and never gains the field edges. A fresh index was unaffected, which is how it slipped past my testing. I have a follow-up branch nearly ready that changes C# extraction again and needs a bump anyway, so I will fold the csharp bump into that PR and call it out in the body; in-place stores then re-extract once for both changes. If you would rather have the bump land immediately as a one-liner, tell me and I will split it out. |
find_usages on a C# field still answered empty after #668, even though the
store held the field's resolved read and write edges. The edges were fine
end to end: extraction emitted them, the resolver bound them to the
enclosing type's own field, and a direct store query showed the authored
story (one constructor write, reads at the real use sites). The gap was
the view layer: isUsageEdgeKind never admitted EdgeReads or EdgeWrites,
so find_usages filtered the rows out and served an empty set over a
correct graph.
Found by re-probing the original false-empty case from my production C#
codebase on a freshly rebuilt store: a repository field read as a call
receiver inside a constructor-initializer lambda had its resolved reads
edge in the store while find_usages returned zero usages. A programmatic
consumer branching on that empty (a dead-code sweep, for example) still
reached the wrong conclusion the #668 fix was meant to prevent.
The change adds EdgeReads and EdgeWrites to isUsageEdgeKind. They are the
value-side usage story for variables and fields by edge.go's own
definition (LHS of assignment emits writes, every other identifier or
selector use emits reads). EdgeAccessesField stays excluded on purpose:
it is the synthesized union of the two riding the same sites, and
admitting it would double-count every usage.
Tests:
with one write, one read, and both accesses_field siblings answers
exactly two usage rows, one of each kind, no duplicates. Written first
and watched fail with zero rows against the old filter.
regression pins for the positions the re-probe went through, all
already emitting correctly today: field reads inside a
constructor-initializer block lambda and behind an expression-lambda
argument, and writes from expression-bodied and static constructors
plus a read in a C# 12 primary-constructor class. They pin that the
emitter half stays correct while the view half changes.
Behavior note: find_usages results for variables and fields grow, since
readers and writers are now listed. That is the intent. Symbols of other
kinds are unaffected: reads/writes edges only ever target value symbols.
Full test suite on Windows matches my recorded baseline exactly, no new
failures.