Skip to content

fix: a base's private member of a generated name is hidden from the value object, not refused - #244

Merged
CaffeinatedCoder merged 2 commits into
release/2.3.1from
fix/private-base-member-refused-2-3-1
Oct 31, 2026
Merged

CaffeinatedCoder merged 2 commits into
release/2.3.1from
fix/private-base-member-refused-2-3-1

Conversation

@CaffeinatedCoder

Copy link
Copy Markdown
Owner

Closes #230

Stacked on #236 (fix/generic-or-ref-overload-refused-2-3-1, #226), which is stacked on #218. Both touch ValueObjectDeclaration.cs and ClosedSetTypes.cs. Merge in order: #218, then #236, then this PR. This PR also answers the sweep's finding against #218 on #205: a set's base class's private Parse(string) was refused.

Problem

The base-member checks that 2.3.1 added read every member of every base, whatever its accessibility:

C# hides a base's private member from the derived type, so it stands in for no generated member. Yet a base record's private MaxValue, Parse, TryFrom or CompareTo(object) failed the build with CMTK1011 or CMTK1008, where 2.3.0 generated the value object. A set's base class's private Parse(string) failed the same way.

Change

  • Each base check counts only a member the type can see (IsAccessibleFrom, Metalama 2026.1). MembersNamed and MethodsWithSignature take the type that looks (seenFrom), and InheritedSeams, the comparable aspect's base loop and the closed set's base reads filter the same way. The closed set's field and property line read Accessibility != Private before and now reads the same as the rest.
  • A value object nested in its base record sees the base's private members, as C# does, so it stays refused. A library's private and internal members were never listed and still build.
  • Generators README (a sentence each under "Write your own members" and "Rules for a closed set"), design/aspects.md, two claims, a CHANGELOG line. No decision record: decision 121 refuses a member "wherever the value object reaches it", and a private one isn't reached.

Tests

  • BaseMemberAccessibility (new) is the input class's catalogue, built in BuildOutcomeTests' consumer. It crosses 6 accessibilities (private, private protected, protected, internal, protected internal, public) with every name the checks read: Value, From, MinValue, MaxValue, Parse/TryParse over string, span and UTF-8, ToString(string?, IFormatProvider?), TryFormat, CompareTo(object), CompareTo(TSelf), TryFrom, Revalidate, and a set's Parse, TryParse and Match. The 19 private rows build. The 80 others and the nested row are refused naming the base. A library's hidden members build.
  • InheritedMembersTests: over a base with every private name, each generated member is what a call reaches. That covers MaxValue == int.MaxValue, parsing, formatting, the ordering without [HandWrittenOrdering], TryFrom(-1) is none, Revalidate, and a set's Parse, TryParse and Match.
  • Reverted: the 19 private rows fail, and the test project's own build refuses its three types. Counting only a base's public members fails the 76 protected and internal rows and the nested one.
  • Locally through tools/BuildVerdict: Generators.Tests 1371 passed, Conventions.Tests 2016, Samples 150.

🤖 Generated with Claude Code

…alue object, not refused

2.3.1's base checks read every member of every base at any accessibility:
Declared over SelfAndBases in ValueObjectDeclaration.GeneratedMembersDeclaredByHand
(#197), InheritedSeams (#195) and the base loop of
ValueObjectComparableAspect.HandWrittenComparisonMembers (#194), and, from
#218, the method lines of ClosedSetTypes.GeneratedMembersDeclaredByHand
(#205). C# hides a base's private member from the derived type, so it
stands in for no generated member: a base record's private MaxValue, Parse,
TryFrom or CompareTo(object) failed the build with CMTK1011 or CMTK1008,
where 2.3.0 generated the value object, and a set's base class's private
Parse(string) with CMTK1011.

Each base check now counts only a member the type can see
(IsAccessibleFrom), on the value object and on the closed set, whose field
and property line read Accessibility != Private and now reads the same. A
value object nested in its base sees the base's private members, as C#
does, and stays refused; a library's private and internal ones were never
listed by Metalama and still build.

Input class (GTS): a base member of a generated member's name or
signature, by accessibility. BaseMemberAccessibility is the catalogue:
private, private protected, protected, internal, protected internal and
public, by each name the checks read (Value, From, MinValue, MaxValue,
Parse and TryParse over string, span and UTF-8, ToString(string?,
IFormatProvider?), TryFormat, CompareTo(object), CompareTo(TSelf), TryFrom,
Revalidate) and a closed set's Parse, TryParse and Match: 108 types in the
consumer of BuildOutcomeTests. The 19 private ones build, the 80 others and
the nested one are refused naming the base, and a library's hidden members
build. InheritedMembersTests holds what the private ones generate.

Reverting the source fails the 19 private rows and refuses the three types
of InheritedMembersTests in the test project's own build; counting only a
base's public members fails the 76 protected and internal rows and the
nested one. No sibling outside these checks: the other base reads
(equality, PrintMembers, an unsealed ToString()) match members C# requires
to be public or protected in a record.

Closes #230

Defect-Class: GTS
Found-By: CA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CaffeinatedCoder CaffeinatedCoder added this to the 2.3.1 milestone Oct 31, 2026
@CaffeinatedCoder CaffeinatedCoder added bug Something isn't working stacked Base is another open PR; merge in order, CI does not run until it targets a release severity/medium Inconsistency or failure in a narrow scenario package/generators Affects CodoMetis.TypeKit.Generators area/value-objects Value-object contracts and what the aspects generate for them labels Oct 31, 2026
Base automatically changed from fix/generic-or-ref-overload-refused-2-3-1 to release/2.3.1 October 31, 2026 11:41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CaffeinatedCoder CaffeinatedCoder removed the stacked Base is another open PR; merge in order, CI does not run until it targets a release label Oct 31, 2026
@CaffeinatedCoder
CaffeinatedCoder merged commit bce9e84 into release/2.3.1 Oct 31, 2026
11 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the fix/private-base-member-refused-2-3-1 branch October 31, 2026 11:52
@CaffeinatedCoder CaffeinatedCoder mentioned this pull request Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/value-objects Value-object contracts and what the aspects generate for them bug Something isn't working package/generators Affects CodoMetis.TypeKit.Generators severity/medium Inconsistency or failure in a narrow scenario

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant