Skip to content

Derived generated class in another assembly loses MarkDirty() in setters (SG3019): base [DirtyTrackingEntity] is not visible across assemblies #58

Description

@kamronbatman

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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions