From 741f5cf9f322e10736e08a72cf024ce383f0a918 Mon Sep 17 00:00:00 2001 From: jsneedles Date: Thu, 1 Oct 2026 10:05:43 -0400 Subject: [PATCH 1/2] V0.2 with ValuesTypeSpan --- CHANGELOG.md | 95 ++++++++++ Directory.Build.props | 2 +- FancyEnumGenerator.slnx | 1 + README.md | 21 +- .../FancyEnumGenerator.Benchmarks/Subjects.cs | 62 +++++- .../ValueBenchmarks.cs | 40 +++- benchmarks/combine_results.cs | 2 +- benchmarks/generate_subjects.cs | 5 +- docs/benchmarks.md | 16 +- docs/how-it-works.md | 16 +- docs/settings.md | 48 +++-- .../FieldMappings.cs | 8 +- .../Beverage.FancyEnum.g.cs | 52 +---- .../FlagWithObsolete.FancyEnum.g.cs | 20 +- .../Fruit.FancyEnum.g.cs | 25 +-- .../Gadget.FancyEnum.g.cs | 24 +-- .../HttpHeader.FancyEnum.g.cs | 111 +---------- .../Permission.FancyEnum.g.cs | 20 +- .../Vegetable.FancyEnum.g.cs | 24 +-- .../Widget.FancyEnum.g.cs | 25 +-- .../FancyEnumGenerator.Examples/Program.cs | 12 +- .../FancyEnumAttributes.cs | 53 ++++-- .../AnalyzerReleases.Unshipped.md | 6 + .../EnumGeneratorDiagnostics.cs | 1 + src/FancyEnumGenerator/EnumModels.cs | 15 +- .../FancyEnumSourceGenerator.Emit.cs | 179 ++++++++---------- .../FancyEnumSourceGenerator.cs | 14 +- .../FancyEnumGenerator.targets | 2 +- .../FancyEnumGenerator.PackageTests/Enums.cs | 14 +- .../Program.cs | 9 +- .../CollectionTests.cs | 72 +++++-- .../Enums/TestEnums.cs | 10 +- .../CompilationTests.cs | 19 +- .../DiagnosticTests.cs | 38 ++++ tests/FancyEnumGenerator.Tests/Scenarios.cs | 31 ++- 35 files changed, 614 insertions(+), 478 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..558f002 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,95 @@ +# Changelog + +All notable changes to FancyEnumGenerator. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and the project uses [Semantic Versioning](https://semver.org/). Before 1.0, a minor version bump (0.1 → 0.2) can +include breaking changes, and each one comes with upgrade notes. + +## [0.2.0] - 2026-10-01 + +Thanks to [the suggestion on Reddit](https://www.reddit.com/r/moderndotnet/comments/1wu7mrh/comment/pd7erju/) to return a +`ReadOnlySpan` from `Values` instead of an inline array: since a span over constants is static data, nothing needs +to be copied. + +### Changed + +- **`Values` is now a `ReadOnlySpan` by default.** The compiler stores the members as static data in your + assembly, so `Values` no longer copies anything or allocates. This needs .NET 7+, except for enums with a 1-byte + underlying type (`byte`, `sbyte`), which now get `Values` on every target, .NET Framework and netstandard2.0 + included. In 0.1, `Values` was a fresh `[InlineArray]` copy on every access and needed .NET 8+. +- **The new `ValuesType` setting chooses what `Values` returns**, with the same precedence as every other setting: on + `[FancyEnum]`, on `[assembly: FancyEnumDefaults]` or as the MSBuild property `FancyEnumValuesType`. The options are + `Span` (the default), `InlineArray` (0.1's default: a storable copy) and `StaticCollection` (a cached + `IReadOnlyList`). `InlineArray` and `StaticCollection` also generate `AsSpan`; with `Span`, `Values` already is + the span. +- **`NoInlineArray` now only controls the `[InlineArray]` struct.** Flags enums lose `ListFlagMembers` with it, and + `ValuesType = InlineArray` isn't available alongside it. +- **The `{Enum}Array` struct is only generated when something uses it:** flags enums, for `ListFlagMembers`, and + `ValuesType = InlineArray`. + +### Removed + +- **`CreateStaticReadonlyCollection`**, on both attributes and as the MSBuild property + `FancyEnumCreateStaticReadonlyCollection`. Use `ValuesType = FancyEnumValuesType.StaticCollection`. + +### Added + +- **`FancyEnumValuesType`**, the enum behind `ValuesType`. +- **HENUM017 (warning):** an explicitly chosen `ValuesType` can't be generated for the current target. That's `Span` on + an enum wider than a byte before .NET 7, `InlineArray` before .NET 8, or `InlineArray` with `NoInlineArray`. + `Values` is left out for that target only, so a multi-targeted project still builds. The default never warns. + +### Fixed + +- **An enum named `Inline` no longer breaks the build.** Its generated `InlineArray` struct shadowed the attribute alias + in the generated file (CS0616). The attribute is now fully qualified. + +### Upgrading from 0.1 + +Most code needs no changes: `foreach (var value in MyEnum.Values)` and `MyEnum.Values[i]` work the same with every +`ValuesType`. What does need attention: + +| If your 0.1 code... | ...do this in 0.2 | +|---|---| +| Sets `CreateStaticReadonlyCollection = true` | Set `ValuesType = FancyEnumValuesType.StaticCollection`. `AsSpan` is unchanged. `Values` becomes an `IReadOnlyList`, where on .NET 8+ it used to be a cached inline-array copy. | +| Sets `true` | Use `StaticCollection`. | +| Stores the default `Values` in a field, keeps it across an `await`, captures it in a lambda or uses it in an iterator | A span can't do that. Set `ValuesType = FancyEnumValuesType.InlineArray` (exactly 0.1's behavior) or `StaticCollection` (no copy at all), or call `.ToArray()`. | +| Assigns the default `Values` to a `Span` | Use `ReadOnlySpan`. The members are read-only data now. | +| Names the `{Enum}Array` type for a non-flags enum, e.g. `FruitArray values = Fruit.Values;` | Use `var` or `ReadOnlySpan`, or set `ValuesType = InlineArray` to keep the struct. | +| Uses `AsSpan` on an enum that only had it because of `CreateStaticReadonlyCollection` | Either keep it with `ValuesType = StaticCollection`, or move to the default and use `Values`, which is the span. | +| Combines `NoInlineArray = true` with `CreateStaticReadonlyCollection = true` | Use `ValuesType = StaticCollection`. Keep `NoInlineArray` only if you also want flags enums without `ListFlagMembers`. | + +To keep 0.1's `Values` behavior across a whole repo while you migrate, set this in `Directory.Build.props`: + +```xml + + InlineArray + +``` + +A note on Debug builds: the JIT doesn't optimize `RuntimeHelpers.CreateSpan` in unoptimized code, so the default +`Values` allocates in a Debug build. Release builds are allocation-free. + +## [0.1.2] - 2026-09-29 + +### Added + +- **`FancyEnumMemberSetItem.NotDefined`:** a string member-set property can fall back to the member's name (`NameOf`, + `NameOfLower`, `NameOfUpper`) for members that don't set it, like `NotDefined` on `[FancyEnumMemberMappingSettings]`. + It works with `ParseFrom`, `CreateTryFormat` and `IncludeUtf8Value`. `DefaultValue` wins if both are set. On a + non-string property it reports HENUM012 and is ignored. + +## [0.1.1] - 2026-09-29 + +### Changed + +- Documentation only: full benchmark results, generated from the BenchmarkDotNet reports, and refreshed performance + numbers in the README. + +## [0.1.0] - 2026-09-29 + +- First public release. + +[0.2.0]: https://github.com/htmlcsstoimage/FancyEnumGenerator/compare/v0.1.2...v0.2.0 +[0.1.2]: https://github.com/htmlcsstoimage/FancyEnumGenerator/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/htmlcsstoimage/FancyEnumGenerator/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/htmlcsstoimage/FancyEnumGenerator/releases/tag/v0.1.0 diff --git a/Directory.Build.props b/Directory.Build.props index be669e5..1a9b91b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ enable enable false - 0.1.2 + 0.2.0