Summary
A [SerializationGenerator] class that derives from a generated, non-ISerializable class in another assembly gets generated setters with no MarkDirty() call, and the build fails with SG3019 (no dirty-tracking target). Within one assembly the same hierarchy works.
Where
SerializableEntityGeneration.BuildModel.cs resolves the dirty-tracking target for a derived class by walking classSymbol.BaseType?.HasDirtyTrackingEntity(compilation), which looks for the [DirtyTrackingEntity] field/property on the base. That member is private on the base (e.g. SpawnerEntry._parent), and private members of a referenced assembly are not imported into the compilation's metadata symbols, so the lookup finds nothing. Inheritance chaining itself works (the derived Serialize/Deserialize are emitted as override and call base.…; pinned by the NestedInheritance snapshot, #56), only the dirty-tracking resolution fails.
Observed
// Assembly B, base in assembly A (ModernUO UOContent)
[SerializationGenerator(0)]
public partial class ModernSpawnerEntry : SpawnerEntry
{
[SerializableField(0)]
private string _script; // generated setter has no MarkDirty(); SG3019
}
Workaround adopted in modernuo/ModernUO#2621: the base exposes protected BaseSpawner Parent => _parent; and every out-of-tree subclass re-declares [DirtyTrackingEntity] private BaseSpawner Owner => Parent;. That works but duplicates the declaration and produces a second, hiding MarkDirty() on the subclass.
Proposed fix
When the base type is generator-attributed (IsSerializableRecursive) but its [DirtyTrackingEntity] member is not visible, use the base's generated public virtual void MarkDirty() as the dirty target (it is public and always emitted for a tracked class): set markDirtyMethod = "MarkDirty()" and emitMarkDirtyMethod = false. Add a snapshot or compilation test with the base in a separate MetadataReference to cover the cross-assembly case, since the in-assembly NestedInheritance fixture cannot reproduce it.
Summary
A
[SerializationGenerator]class that derives from a generated, non-ISerializableclass in another assembly gets generated setters with noMarkDirty()call, and the build fails with SG3019 (no dirty-tracking target). Within one assembly the same hierarchy works.Where
SerializableEntityGeneration.BuildModel.csresolves the dirty-tracking target for a derived class by walkingclassSymbol.BaseType?.HasDirtyTrackingEntity(compilation), which looks for the[DirtyTrackingEntity]field/property on the base. That member isprivateon the base (e.g.SpawnerEntry._parent), and private members of a referenced assembly are not imported into the compilation's metadata symbols, so the lookup finds nothing. Inheritance chaining itself works (the derivedSerialize/Deserializeare emitted asoverrideand callbase.…; pinned by theNestedInheritancesnapshot, #56), only the dirty-tracking resolution fails.Observed
Workaround adopted in modernuo/ModernUO#2621: the base exposes
protected BaseSpawner Parent => _parent;and every out-of-tree subclass re-declares[DirtyTrackingEntity] private BaseSpawner Owner => Parent;. That works but duplicates the declaration and produces a second, hidingMarkDirty()on the subclass.Proposed fix
When the base type is generator-attributed (
IsSerializableRecursive) but its[DirtyTrackingEntity]member is not visible, use the base's generatedpublic virtual void MarkDirty()as the dirty target (it is public and always emitted for a tracked class): setmarkDirtyMethod = "MarkDirty()"andemitMarkDirtyMethod = false. Add a snapshot or compilation test with the base in a separateMetadataReferenceto cover the cross-assembly case, since the in-assemblyNestedInheritancefixture cannot reproduce it.