Skip to content

feat: cover ReactiveUI.SourceGenerators' ObservableAsProperty and IViewFor behaviour - #142

Merged
glennawatson merged 1 commit into
mainfrom
feat/sourcegenerators-compat
Sep 25, 2026
Merged

glennawatson merged 1 commit into
mainfrom
feat/sourcegenerators-compat

Conversation

@glennawatson

Copy link
Copy Markdown
Contributor

Summary

[ObservableAsProperty] and view registration cover what ReactiveUI.SourceGenerators did, so it can drop its copies.

  • [ObservableAsProperty] takes InitialValue, ReadOnly and UseProtected. The property returns its initial value until its helper is assigned. A non-string initial value is an expression, evaluated once, bound with the declaring file's usings. A non-nullable string property starts as string.Empty.
  • RXUIBIND018 and a code fix move ReactiveUI.SourceGenerators' other forms onto partial properties. A field, a method returning an observable, and an observable property marked [ObservableAsProperty] get nothing generated. The fix rewrites a field as a property, carrying its initializer and Inheritance across. It gives a method or observable property a property of its own, assigned in a written InitializeOAPH method. The attribute is valid on those members so older code compiles, and PropertyName and Inheritance exist for the fix.
  • RXUIBIND019 reports a marked method that takes parameters.
  • Views declared with ReactiveUI.SourceGenerators' [IViewFor<T>] or [IViewFor("TypeName")] register. That generator adds IViewFor<T> in output other generators cannot see, so the attribute is read directly and the lookup checks for the interface when it resolves the view. RegistrationType LazySingleton and Constant keep one instance.
  • Views register in a ReactiveUI.Binding.Reactive consumer. Only the lean IViewFor<T> was matched, so a System.Reactive consumer got no generated view lookup.
  • Every DebuggerDisplay starts with the name of its type, and all of them are ASCII.

Why

ReactiveUI.SourceGenerators is removing [ObservableAsProperty] and [IViewFor] view registration because this library provides them (reactiveui/ReactiveUI.SourceGenerators#501). The options, forms and attributes above were the gaps. A debugger showing a bare value gave no hint of what was being looked at.

Breaking changes

  • A non-nullable string property marked [ObservableAsProperty] now returns string.Empty rather than null before its helper is assigned.
  • [ObservableAsProperty] on a field, a method or a non-partial property now reports RXUIBIND018 rather than doing nothing silently.
  • Not taken from ReactiveUI.SourceGenerators: ReadOnly defaults to false, so existing code that assigns a helper outside a constructor keeps compiling. The [Obsolete] RegisterViewsForViewModelsSourceGenerated shim is not generated here, because it would declare the same type in every consumer assembly.

How this was verified

  • Tests cover each option at run time in both runtime flavours, every field shape from ReactiveUI.SourceGenerators' own tests rewritten by the fix and then run, and the method and observable-property forms observed through WhenAnyValue.
  • Views declared by attribute are resolved end to end, with a stand-in generator adding the interface the way ReactiveUI.SourceGenerators does. A System.Reactive consumer's views resolve too.
  • Ran the Linux examples suite and the Windows examples on a real Windows machine.

Notes for the reviewer

  • Start with ObservableAsPropertyGenerator.cs, ViewRegistrationExtractor.cs, and ObservableAsPropertyCodeFixProvider.cs with ObservableAsPropertyRewriter.cs.
  • Mechanical: the DebuggerDisplay edits (a Roslyn rewrite adding the type name) and the regenerated PublicAPI.txt baselines, which change with them.
  • The generator test projects reference the analyzer under the analyzer extern alias, because the analyzer compiles its own copies of the generator's shared internal types.
  • Out of scope: attribute forwarding and the CS0657/CA1822 suppressions from the request.

Checklist

  • I have read the Contribute guide
  • The PR title follows Conventional Commits
  • Tests cover this change, or the summary says why they do not
  • New or changed public API has XML documentation

@glennawatson
glennawatson enabled auto-merge (squash) September 25, 2026 18:40
…ewFor behaviour

- [ObservableAsProperty] takes InitialValue, ReadOnly and UseProtected. An
  initial value is returned until the helper is assigned; a non-string one is
  an expression bound with the declaring file's usings.
- RXUIBIND018 and a code fix rewrite the older field, method and observable
  property forms as partial properties; RXUIBIND019 reports a marked method
  with parameters.
- Views declared with ReactiveUI.SourceGenerators' [IViewFor] register, and
  views register in a ReactiveUI.Binding.Reactive consumer.
- Every DebuggerDisplay starts with the name of its type.
@glennawatson
glennawatson force-pushed the feat/sourcegenerators-compat branch from 84a63d9 to 70caf4c Compare September 25, 2026 18:43
@codecov

codecov Bot commented Sep 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.18%. Comparing base (1c622f6) to head (70caf4c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #142      +/-   ##
==========================================
+ Coverage   99.15%   99.18%   +0.02%     
==========================================
  Files         370      374       +4     
  Lines       13416    13794     +378     
  Branches     1768     1880     +112     
==========================================
+ Hits        13303    13681     +378     
  Misses        113      113              

☔ 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.

@sonarqubecloud

Copy link
Copy Markdown

@glennawatson
glennawatson merged commit ad104c5 into main Sep 25, 2026
22 checks passed
@glennawatson
glennawatson deleted the feat/sourcegenerators-compat branch September 25, 2026 19:07
@github-actions

Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Oct 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant