Skip to content

query: find_usages serves value-side reads/writes of variables and fields - #671

Merged
zzet merged 1 commit into
zzet:mainfrom
pbednarcik:fix/usages-field-read-edges
Aug 26, 2026
Merged

query: find_usages serves value-side reads/writes of variables and fields#671
zzet merged 1 commit into
zzet:mainfrom
pbednarcik:fix/usages-field-read-edges

Conversation

@pbednarcik

Copy link
Copy Markdown
Contributor

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:

  • internal/query/find_usages_field_reads_test.go pins the view: a field
    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.
  • internal/parser/languages/csharp_field_identifier_test.go grows two
    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.

…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.
@zzet

zzet commented Aug 26, 2026

Copy link
Copy Markdown
Owner

find_usages on a C# field still answered empty after #668

@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)

@zzet
zzet merged commit 3eb51c6 into zzet:main Aug 26, 2026
10 checks passed
@pbednarcik

Copy link
Copy Markdown
Contributor Author

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants