Skip to content

fix(generator): correct which call sites the dispatch claims - #91

Merged
glennawatson merged 2 commits into
mainfrom
fix/declaring-class-binding-calls
Sep 10, 2026
Merged

fix(generator): correct which call sites the dispatch claims#91
glennawatson merged 2 commits into
mainfrom
fix/declaring-class-binding-calls

Conversation

@glennawatson

@glennawatson glennawatson commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix. Two defects in which call sites a dispatch claims.

What is the new behavior?

Every call site an interception build can name is claimed.

  • Call sites spelling the same selectors each carry their own attribute, so binding the same pair of properties from two places generates both. Collapsing stays where it belongs: expression-text dispatch keys on the selectors as written, so its later branches really are unreachable.
  • The rule is stated once on the feature snapshot rather than decided separately by each API.

A binding call written through the stub's declaring class compiles.

  • ReactiveUIBindingExtensions.WhenChanged(vm, x => x.Name) and the same shape on every other binding API generate no dispatch, and the call takes the runtime engine like any other shape the generator cannot serve.
  • The shared receiver guard declines a static type alongside a type parameter, so every API is covered from one place: each extractor reads the bound object through it.

What is the current behavior?

A repeated call site silently loses its generated binding. Under interception the group is collapsed before the attributes are emitted, so the second call site is left on the runtime expression engine while the one beside it is generated - a whole screen of a view at a time, with the build green.

A call naming the class fails the consumer's whole build. The class itself is read as the observed object, so the emitted worker declares a parameter of a static type and takes it as a type argument: CS0721 and CS0718 on generated code the consumer cannot edit, which fails every unrelated call site in the project with it.

What might this PR break?

None.

  • Nothing could serve the call sites that are now declined. A call that names the class resolves against that class's members, so neither the generated overload nor an interceptor matching the call written on an instance is a candidate for it.
  • Interception now emits one binding method per claimed call site where two were spelled identically, which is what file-and-line dispatch already does.

Checklist

  • I have read the Contribute guide
  • Tests have been added or updated (for bug fixes / features)
  • Docs have been added or updated (for bug fixes / features)
  • Changes target the main branch
  • PR title follows Conventional Commits

Additional information

One test per extractor pins the declined shape, so an API whose argument reading shifts by one is caught where it would otherwise emit uncompilable code. The interception tests emit the assembly, because that is where the compiler checks an interceptor against the call it replaces.

- A call written as ReactiveUIBindingExtensions.WhenChanged(vm, x => x.Name)
  puts that class where the observed object goes, so the emitted worker declared
  a parameter of a static type and took it as a type argument. The consumer's
  whole build failed on generated code they cannot edit (CS0721, CS0718), every
  unrelated call site in the project with it.
- The shared receiver guard declines a static type alongside a type parameter,
  which covers every API from one place: each extractor reads the bound object
  through it.
- Nothing could have served those call sites anyway. A call that names the class
  resolves against that class's members, so neither the generated overload nor an
  interceptor matching the call written on an instance is a candidate, and the
  call belongs on the runtime stub.
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.48%. Comparing base (8472562) to head (cf6e2b4).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #91   +/-   ##
=======================================
  Coverage   99.48%   99.48%           
=======================================
  Files         250      251    +1     
  Lines       10651    10652    +1     
  Branches     1180     1181    +1     
=======================================
+ Hits        10596    10597    +1     
  Misses         42       42           
  Partials       13       13           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Call sites spelling the same selectors were collapsed to one before the
  interceptors were emitted, so the second one carried no attribute: it took the
  runtime engine while the call site beside it was generated. Binding the same
  pair of properties from two places is ordinary, so this bit whole screens of a
  view at a time.
- Collapsing stays where it belongs. Expression-text dispatch keys on the
  selectors as written, so the later branches are unreachable and only drag a
  binding method along; an interceptor names the call site it replaces, so
  nothing may be dropped.
- The rule is stated once on the feature snapshot rather than per API. Four
  emitters decided it separately, and the one that forgot lost a binding without
  failing to compile.
- The tests emit the assembly, because that is where the compiler checks an
  interceptor against the call it replaces.
@glennawatson glennawatson changed the title fix(generator): decline a binding call named through its declaring class fix(generator): correct which call sites the dispatch claims Sep 10, 2026
@sonarqubecloud

Copy link
Copy Markdown

@glennawatson
glennawatson merged commit 74bd257 into main Sep 10, 2026
12 checks passed
@glennawatson
glennawatson deleted the fix/declaring-class-binding-calls branch September 10, 2026 01:30
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.

1 participant