Repository navigation
Generators: one signature identity for every hand-written-member check, the generated members derived from the aspects and marked by an attribute #239
Description
Why
Seven 2.3.1 bugs (#191, #195, #196, #197, #205, #226, #230) are one question answered differently in several places: is this member the user wrote the one a generator introduces? ValueObjectDeclaration.MethodsWithSignature, ClosedSetTypes.SameParameters, MembersNamed and the base-type walks each decide arity, ref kind, nullability and accessibility by their own rules. The generated members' names are hand-kept lists (ValueObjectDeclaration.cs:182, ClosedSetTypes.cs:35), and AGENTS.md notes that a new generated member left off them is "kept silently".
Proposal
- One signature identity used by every check, following C#'s overloading rules: name, arity, each parameter's type without its nullability, and its ref kind (
out/ref/inagainst by-value differ,refagainstoutclash), plus whether the member is visible from the value object (a private base member is not its own). - Derive the generated members from what the aspects introduce, so that the check and the generation cannot disagree, instead of name lists kept in step by hand.
- Mark every generated member with a TypeKit-owned attribute in the base package (Metalama-free), naming the contract member it implements. The signature identity still decides the check before generation, where nothing carries the attribute yet; the attribute answers what that check can only infer after it: whether a base record's member from another assembly was generated or written by hand (the A base record's explicit Create from another project leaves two rule sets #195/A base record's public implementation of a generated member is kept as the value object's, or crashes the aspect #197 family), and, for the analyzers and the run-time satellites, which members are generated, without name lists (discovery by attribute, as
GeneratedValueObjectAttribute<TValueObject, T>does for types). Not[GeneratedCode]: Roslyn analyzers skip code marked with it by default, which would silence the CMTK rules and the consumer's own analyzers on those members.
Done when
Every hand-written-member check goes through the one identity; the name lists are gone or generated from the aspects' introductions; every generated member carries the attribute (GeneratedSurface.verified.txt), and a base record's generated member from another assembly is told from a hand-written one by it; the attribute has a claim or a surface waiver; the regression rows of #191 to #197, #205, #226 and #230 pass unchanged; no build outcome changes beyond those fixes.
Outcome (2026-10-31, #279)
- Separable, so they land as two commits in One signature identity for every hand-written-member check, and [GeneratedMember] on every generated member #279, the identity (
fix:) before the attribute (feat:), after the design in decision 128. - The identity is C#'s, and it moves some outcomes, each measured against
af3f95e: a property or field of a generated method's name (own or a base's), a set's ownName()and a set base's nested converter type now fail with CMTK1011 instead of LAMA0506/LAMA0531/LAMA0611. A base record'sTryFrom(int?)over anint, a set'sParse(string, bool?), a base record'sMinValue()/Value()/_value()and an entry point's overload now build where they were CMTK1011. No build that built fails. - Every generated member carries the attribute, with one exception: Metalama 2026.1 applies no attribute to an override it writes over a record's implicit
ToString()(andEquals/GetHashCode()over a base record), and refusesIntroduceAttributethere. - "A base record's generated member from another assembly is told from a hand-written one by it": no base type can carry a generated member today (value objects and closed sets are sealed, and an abstract one is not generated), so this can't be exercised with a real base.
IsHandWrittenreads the attribute, andGeneratedMemberAttributeTestsshows that in another assembly (the probes) every generated member carries it and no hand-written one does.
Activity
[-]Generators: one signature identity for every hand-written-member check, and the generated members derived from the aspects[/-][+]Generators: one signature identity for every hand-written-member check, the generated members derived from the aspects and marked by an attribute[/+]CaffeinatedCoder commentedon Oct 31, 2026
Landed on
release/2.4.0by #279 (merge commit 020e48c), with decision 128: one signature identity for the hand-written-member checks, and[GeneratedMember]on every generated member.