Skip to content

Generator: Implement Prototype Pattern #31

@JerrettDavis

Description

@JerrettDavis

Summary

Add a source generator that produces boilerplate-free, GoF-consistent Prototype pattern implementations, generating safe cloning APIs with configurable strategies for mutable references.

The generator lives in PatternKit.Generators and emits self-contained C# with no runtime PatternKit dependency.

Primary goals:

  • Generate Clone()/Copy() methods that are correct and explicit.
  • Provide safe defaults to avoid aliasing mutable members.
  • Support shallow vs deep cloning where deterministically possible.
  • Support records and structs cleanly.

Motivation / Problem

Cloning is where bugs go to reproduce:

  • shallow clones that accidentally share mutable lists
  • “deep clone” promises that lie
  • reflection-based cloning that breaks trimming/AOT

We want generator-emitted clones that are:

  • deterministic
  • reflection-free
  • explicit about what is shallow vs deep

Supported Targets (must-have)

The generator must support:

  • partial class
  • partial struct
  • partial record class
  • partial record struct

Proposed User Experience

A) Default clone (safe-by-default)

[Prototype]
public partial record class UserProfile
{
    public string Name { get; init; } = "";
    public List<string> Tags { get; init; } = new();
}

Generated (representative shape):

public partial record class UserProfile
{
    public UserProfile Clone();
}

Default behavior:

  • value types copied
  • strings copied (safe)
  • mutable reference types produce a diagnostic unless a strategy is provided

B) Per-member strategy

[Prototype]
public partial record class UserProfile
{
    public string Name { get; init; } = "";

    [PrototypeStrategy(PrototypeCloneStrategy.Clone)]
    public List<string> Tags { get; init; } = new();
}

Generator emits new List<string>(Tags) for Clone strategy when T is cloneable or has copy ctor patterns (rules below).

C) Custom clone hook

[Prototype]
public partial class Complex
{
    public Widget Widget { get; set; }

    [PrototypeStrategy(PrototypeCloneStrategy.Custom)]
    public Widget Widget2 { get; set; }

    private static partial Widget CloneWidget2(Widget value);
}

Attributes / Surface Area

Namespace: PatternKit.Generators.Prototype

  • [Prototype] on the prototype type

    • PrototypeMode Mode (default: ShallowWithWarnings)
    • string CloneMethodName (default: Clone)
  • [PrototypeIgnore] exclude a member

  • [PrototypeInclude] include only marked members (if IncludeExplicit mode)

  • [PrototypeStrategy] per-member strategy

Enums:

  • PrototypeMode: ShallowWithWarnings, Shallow, DeepWhenPossible
  • PrototypeCloneStrategy: ByReference, ShallowCopy, Clone, DeepCopy, Custom

Semantics (must-have)

Member selection

  • Default: include all eligible instance fields/properties with getters.
  • Support include-explicit via [Prototype(IncludeExplicit=true)] (optional if you prefer to split; but consistent with other generators).

Clone construction

  • For records: prefer with { ... } when feasible.

  • Otherwise:

    • use copy constructor if available
    • else use parameterless ctor + member assignment where writable

If none are possible, emit a diagnostic and do not generate broken code.

Strategies

Baseline rules:

  • value types: copy

  • string: copy

  • reference types:

    • default: ByReference with warning under ShallowWithWarnings

Strategy Clone (v1):

  • Supported when:

    • type implements ICloneable (discouraged but common), OR
    • has an accessible Clone() method returning same type, OR
    • has a copy constructor T(T other)

Collections:

  • List<T> with Clone strategy:

    • new List<T>(old)
    • if T requires clone, deep behavior is v2 unless explicitly configured.

Strategy DeepCopy:

  • v2 unless very limited and provable.

Strategy Custom:

  • requires user-provided partial method Clone<MemberName>(T value).

Diagnostics (must-have)

Stable IDs, actionable:

  • PKPRO001 Type marked [Prototype] must be partial.
  • PKPRO002 Cannot construct clone target (no supported clone construction path).
  • PKPRO003 Unsafe reference capture: member cloned by reference (warning).
  • PKPRO004 Requested Clone strategy but no clone mechanism found.
  • PKPRO005 Custom strategy requires partial clone hook, but none found.
  • PKPRO006 Include/Ignore attribute misuse.

Generated Code Layout

  • TypeName.Prototype.g.cs

Determinism:

  • stable ordering by member name.

Testing Expectations

  • Records clone with with when possible.

  • Class clone via ctor+assignment works.

  • Diagnostics:

    • missing construction path
    • unsafe reference warnings
    • missing custom hook
  • Strategy behavior:

    • list clone produces new list
    • by-reference leaves same reference

Acceptance Criteria

  • [Prototype] works for class/struct/record class/record struct.
  • Generated clone method is deterministic and reflection-free.
  • Safe-by-default warnings for mutable references.
  • Per-member strategies supported (ByReference/Clone/Custom v1).
  • Diagnostics cover constructibility and strategy gaps.
  • Tests cover records, classes, and strategy behaviors.

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions