feat!: ship the attributes in a public library instead of generating them into each project - #503
Merged
Merged
Conversation
…them into each project Every generator injected its attributes and enums into each consuming project as internal types. Two projects that share internals through InternalsVisibleTo then saw two copies of ReactiveUI.SourceGenerators.ReactiveAttribute and failed with CS0436. The attributes and the AccessModifier, PropertyAccessModifier and InheritanceModifier enums are now public types in ReactiveUI.SourceGenerators.dll, built for net8.0-net11.0 and net462-net481. They are [Conditional], so a consuming assembly keeps no reference to it. The generators no longer register post-initialization output. The ReactiveUI.SourceGenerators project is now that library and the package consumers install. The Roslyn 4.8, 4.14 and 5.0 builds are renamed ReactiveUI.SourceGenerators.Roslyn.dll and bundled under analyzers/, each band with the code fixes, which the package previously referenced only as a dependency that excluded its analyzers. BREAKING CHANGE: the package is no longer a development dependency, because consumers compile against its attributes. A PackageReference whose IncludeAssets omits compile must drop that line. The attributes' named properties have set accessors instead of init.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #503 +/- ##
==========================================
- Coverage 96.94% 96.45% -0.50%
==========================================
Files 69 71 +2
Lines 3857 3578 -279
Branches 503 506 +3
==========================================
- Hits 3739 3451 -288
- Misses 118 127 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The generated file opened with `using DynamicData;` although the property it writes is fully qualified and only exposes the ReadOnlyObservableCollection<T> field. A project without DynamicData, and so without System.Reactive, failed with CS0246. The using is gone, so the field can be filled by any means. Closes #489
…scheduler [ReactiveCommand(RunInBackground = true)] now starts a task-returning method with Task.Run, passing the command parameter and CancellationToken through, so the code before its first await no longer runs on the calling thread. It previously applied to synchronous methods only and was ignored for tasks. BackgroundScheduler names the scheduler a synchronous background command runs on: a scheduler member of the class or a built-in ReactiveUI scheduler, as for OutputScheduler. Setting it implies RunInBackground. Without an output scheduler or can-execute, the call names `canExecute: null`, since ReactiveUI 24 has no overload taking only the execute delegate and a background scheduler. Closes #347
The generators read the attributes from source and a consuming assembly never instantiates them, so no generator test ran their constructors. The tests construct each attribute and check what it holds.
ChrisPulman
approved these changes
Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The attributes are public types in the package's own library, so the generators no longer declare them in every consuming project; task commands can also run in the background, and
[BindableDerivedList]output no longer needs DynamicData.[Reactive],[ReactiveCommand]and the other attributes, with theAccessModifier,PropertyAccessModifierandInheritanceModifierenums, ship inReactiveUI.SourceGenerators.dll. It targets net8.0-net11.0 and net462-net481, keeps the existing namespaces, and is referenced by consumers from the package'slib/folder.[Conditional], so a consuming assembly keeps no reference to the library. The generators read them from source; definingREACTIVEUI_SOURCEGENERATORS_KEEP_ATTRIBUTESkeeps them in metadata.analyzers/. The Roslyn 4.8, 4.14 and 5.0 builds are nowReactiveUI.SourceGenerators.Roslyn.dll, and each band carries the code fixes, which previously arrived only through a dependency that excluded its analyzers. Consumers now see RXUISG0016 and RXUISG0020.[ReactiveCommand(RunInBackground = true)]starts a task-returning method withTask.Run. The command parameter andCancellationTokenare passed through, so the code before the firstawaitno longer runs on the calling thread; previously the option was ignored for tasks.[ReactiveCommand(BackgroundScheduler = ...)]chooses the scheduler a synchronous background command runs on. It accepts a scheduler member or a built-in ReactiveUI scheduler, asOutputSchedulerdoes, and impliesRunInBackground.[BindableDerivedList]output no longer opens withusing DynamicData;. The generated property only exposes the field, so a project without DynamicData or System.Reactive can use it.Why
Projects that share internals through
InternalsVisibleTofailed with CS0436 when both used the generators, and the attribute namespace only existed when the generator ran.ReactiveUI.SourceGenerators.ReactiveAttributeand friends, so an assembly granted access to another's internals saw two copies of each.ReactiveUI.SourceGeneratorsas missing; it now comes from a referenced assembly.initaccessors, which do not compile on .NET Framework without anIsExternalInitpolyfill.Task.Run, and the background scheduler could not be chosen.[BindableDerivedList]forced a DynamicData reference, and with it System.Reactive, on projects that fill the collection by other means.Closes #371
Closes #347
Closes #489
Breaking changes
The package is no longer a development dependency, because consumers compile against its attributes.
PackageReferencewith an<IncludeAssets>line that omitscompile(older installs added one) must drop that line:<PackageReference Include="ReactiveUI.SourceGenerators" Version="..." PrivateAssets="all" />.setaccessors instead ofinit; attribute syntax is unchanged.[Reactive]attributes report RXUISG0020 as a warning.RunInBackground = truenow starts on the thread pool; remove the option to keep it on the calling thread.How this was verified
The generator tests compile against the attributes library, as a consumer does; new tests run generated task commands to check the thread they start on, and compile
[BindableDerivedList]without DynamicData; the packed nupkg was exercised by hand.InternalsVisibleToto an app, both using[Reactive], reproduces CS0436 on the previous package and builds and runs on this one; a net472 consumer builds. The VS2022 editor behaviour in [Bug]: Namespace ReactiveUI.SourceGenerators doesn't not exists in VS2022 when updated to 2.6.1 #371 was not reproduced here.Notes for the reviewer
Start with
src/ReactiveUI.SourceGenerators/and its project file, thenReactiveCommandGenerator.Execute.cs; most of the rest is mechanical.ReactiveUI.SourceGenerators.csproj,AttributeDefinitions.cs, the generators'Initializemethods,TestHelper.cs, and the command generator'sWriteExecuteArgumentandWriteSchedulerArguments.*Attr.verified.csand*AccMod.verified.cssnapshots of the injected files, the one-lineusing DynamicData;removal across theDERIVEDLISTsnapshots, the project references added to the test, sample and benchmark projects, and the Roslyn projects'AssemblyName.CreateRunInBackground(execute, backgroundScheduler)overload, so a background scheduler without can-execute or output scheduler is written withcanExecute: null, which also binds on ReactiveUI 23.BackgroundSchedulerdoes not apply to task-returning methods, which always start on the thread pool, or to observable-returning methods.ReactiveUI.SourceGenerators.Analyzers.CodeFixespackage is still packable on its own; nothing depends on it any more.Checklist