Skip to content

Write enum values by name, from the assembly that declares the enum - #108

Merged
Mpdreamz merged 1 commit into
mainfrom
enum-names
Nov 15, 2026
Merged

Mpdreamz merged 1 commit into
mainfrom
enum-names

Conversation

@Mpdreamz

Copy link
Copy Markdown
Contributor

Closes two of the known gaps:

  • An enum value in a default, a constant or an attribute argument was written as a cast of its number, (ApiSplit)0.
  • An attribute's enum from another assembly was decoded as if it were an int. That garbled any argument whose enum wasn't an int.

What changes

The reader now finds the enum in the assembly that declares it. It looks in this order:

  1. the assembly being read;
  2. that assembly's folder;
  3. any folders a new ApiReaderOptions.ReferenceDirectories adds;
  4. the installed .NET runtime.

It follows type forwarders along the way, such as System.Runtime's to System.Private.CoreLib. The result, from the new enums sample:

const Access Default = Access.Read
[Sized(Size.Large)]                                     // a byte enum, decoded with its real underlying type
[EditorBrowsable(EditorBrowsableState.Never)]
void Open(FileShare share = FileShare.Read | FileShare.Delete, StringComparison comparison = StringComparison.OrdinalIgnoreCase,
          Size size = Size.Large, Access access = Access.ReadWrite | Access.Execute, Access none = Access.None, Access odd = (Access)64, Size? maybe = Size.Small)
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
  • Flags: combinations are written with |.
  • No match: a value with no member, such as (Access)64, or an enum that can't be found, stays a cast.
  • No runtime lookup: ApiReaderOptions { SearchRuntime = false } turns off the runtime lookup.
  • The comparison: a cast and a name of the same enum count as the same value. One machine may find an enum that another can't, and that shouldn't read as a change. The known limit: going from (Access)64 to Access.Read isn't reported either.

api/ gains the new public ApiReaderOptions and its two Read overloads. The change is additive.

Cost

Referenced assemblies are opened only when an enum needs them, and only their metadata is read. The assembly being read is indexed once; the others are searched in place. Reading CoreLib is unchanged at 107 MB. System.Collections.Immutable allocates 3.2 MB (it was 3.7) and takes about 4.5 ms, up from 1.8 ms, because it now opens System.Runtime and CoreLib's metadata. That's a fixed cost of a few milliseconds per read.

Tests

  • New: the enums sample; a test that an enum that can't be found stays a cast; and a test that a name and a cast compare as no change. Taking out that comparison rule fails the test.
  • Existing: the CsCheck soak (1,500 libraries per property, 500 pairs), the Roslyn oracle and the framework round trips pass. mise run ci passes: 88 library tests and 630 tool tests.

Not done: in solution mode, the tool could also look in the folders of a project's NuGet dependencies. Today an enum that comes from a package stays a cast unless its assembly sits next to the build.

🤖 Generated with Claude Code

A default, a constant or an attribute argument of an enum type was written as a cast of its number, (ApiSplit)0, and
an attribute's enum from another assembly was decoded as if it were an int. The reader now finds the enum, in the
assembly being read, its folder, the folders ApiReaderOptions adds, or the installed .NET runtime, following type
forwarders such as System.Runtime's: ApiSplit.None, StringComparison.Ordinal, FileShare.Read | FileShare.Delete for
flags, and the real underlying type for decoding, so [Sized(Size.Large)] on a byte enum reads right. An enum that
cannot be found, or a value with no member, stays a cast.

The comparison counts a cast and a name of the same enum as the same value, since one machine can find an enum that
another cannot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Mpdreamz Mpdreamz added the feature A new capability (minor bump) label Nov 15, 2026
@Mpdreamz
Mpdreamz merged commit 97a00ba into main Nov 15, 2026
7 of 8 checks passed
@Mpdreamz
Mpdreamz deleted the enum-names branch November 15, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature A new capability (minor bump)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant