feat: add [OptimizedEnumIndex] attribute for generated property lookups#5
Conversation
Introduces OptimizedEnumIndexAttribute, which can be placed on properties
in intermediate OptimizedEnum base classes to generate pre-built dictionary
lookups (From{PropertyName} / TryFrom{PropertyName}) on every concrete
subclass. Supports any IEquatable<T> property type; string properties
respect a configurable StringComparison (default: Ordinal).
Also bumps VersionPrefix to 1.2.0 and updates local pack scripts to use
a -local.<n> pre-release suffix instead of a fourth version segment.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new opt-in indexing feature for OptimizedEnum-derived types via a new [OptimizedEnumIndex] attribute applied to properties on intermediate base classes, enabling the generator to emit per-property From{PropertyName} / TryFrom{PropertyName} dictionary lookups on concrete subclasses. It also bumps the package version to 1.2.0 and updates generator tests/snapshots accordingly.
Changes:
- Added
OptimizedEnumIndexAttribute(with configurableStringComparisonforstringkeys). - Extended the generator pipeline/model/template to collect indexed base properties and emit corresponding dictionaries + lookup methods.
- Added new diagnostic
OE0202and expanded snapshot/verification tests; bumpedVersionPrefixto1.2.0.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.Warning_OE0202_IndexProperty_NonEquatableType_IsEmitted#MyApp.Domain.Status.g.verified.cs | Snapshot for codegen when OE0202 is produced (index skipped). |
| tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.Warning_OE0202_IndexProperty_NonEquatableType_IsEmitted.verified.txt | Snapshot for OE0202 diagnostic output. |
| tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.IndexedProperty_StringIndex_GeneratesLookupMethods#MyApp.Domain.ForceAlignment.g.verified.cs | Snapshot validating generated lookup for a single indexed string property. |
| tests/LayeredCraft.OptimizedEnums.Generator.Tests/Snapshots/GeneratorVerifyTests.IndexedProperty_MultipleIndexes_GeneratesAllLookupMethods#MyApp.Domain.ForceAlignment.g.verified.cs | Snapshot validating multiple indexed properties with differing StringComparison. |
| tests/LayeredCraft.OptimizedEnums.Generator.Tests/GeneratorVerifyTests.cs | Adds verification tests covering indexed-property generation and OE0202. |
| src/LayeredCraft.OptimizedEnums/OptimizedEnumIndexAttribute.cs | New public attribute API for opting properties into index generation. |
| src/LayeredCraft.OptimizedEnums.Generator/Templates/OptimizedEnum.scriban | Emits per-index dictionaries and From*/TryFrom* methods. |
| src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs | Collects indexed properties from base chain; emits OE0202; resolves StringComparer. |
| src/LayeredCraft.OptimizedEnums.Generator/Models/IndexedPropertyInfo.cs | New model record describing indexed properties for template emission. |
| src/LayeredCraft.OptimizedEnums.Generator/Models/EnumInfo.cs | Carries indexed properties through the generator pipeline. |
| src/LayeredCraft.OptimizedEnums.Generator/Emitters/EnumEmitter.cs | Passes indexed property model into Scriban template. |
| src/LayeredCraft.OptimizedEnums.Generator/Diagnostics/DiagnosticDescriptors.cs | Adds OE0202 descriptor. |
| src/LayeredCraft.OptimizedEnums.Generator/AnalyzerReleases.Unshipped.md | Records newly added diagnostic OE0202. |
| Directory.Build.props | Bumps VersionPrefix to 1.2.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs
Show resolved
Hide resolved
src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs
Outdated
Show resolved
Hide resolved
src/LayeredCraft.OptimizedEnums.Generator/Diagnostics/DiagnosticDescriptors.cs
Outdated
Show resolved
Hide resolved
src/LayeredCraft.OptimizedEnums.Generator/Templates/OptimizedEnum.scriban
Show resolved
Hide resolved
- OE0203: emit warning and skip index for static, private, indexer, or
write-only properties marked [OptimizedEnumIndex]
- OE0204: emit warning and skip index when property name conflicts with
reserved generated members (Name → s_byName/FromName/TryFromName,
Value → s_byValue/FromValue/TryFromValue)
- Fix OE0202 message to say "the index will not be generated" rather
than implying fallback to reference equality
- Add [NotNullWhen(true)] to TryFromName and TryFromValue for
consistency with TryFrom{PropertyName} methods
- Add tests for OE0203 (private property, static property) and OE0204
(reserved name collision)
- Update all affected snapshots
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tNullWhen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 38 out of 38 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/LayeredCraft.OptimizedEnums.Generator/Providers/EnumSyntaxProvider.cs
Show resolved
Hide resolved
src/LayeredCraft.OptimizedEnums.Generator/Templates/OptimizedEnum.scriban
Show resolved
Hide resolved
…n compatibility - Move seenNames.Add() in CollectIndexedProperties to after all validation checks so an invalid nearer-base [OptimizedEnumIndex] property cannot shadow a valid one higher in the inheritance chain - Add HasNotNullWhenAttribute to EnumInfo, detected via compilation.GetTypeByMetadataName at transform time; template now conditionally emits [NotNullWhen(true)] on TryFrom* parameters so generated code compiles on netstandard2.0 targets where the attribute is absent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 38 out of 38 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| member.Parameters.Length > 0 || | ||
| member.GetMethod is null || | ||
| member.DeclaredAccessibility == Accessibility.Private || | ||
| member.GetMethod.DeclaredAccessibility == Accessibility.Private) | ||
| { |
There was a problem hiding this comment.
OE0203 validation only checks for private accessibility. If the indexed property is internal, private protected (ProtectedAndInternal), or otherwise inaccessible from the concrete enum’s assembly/type (e.g., when the intermediate base class comes from a referenced assembly), the generator can still emit s_by{PropertyName} initialization accessing that property, which will fail to compile. Consider validating accessibility using Roslyn’s accessibility APIs (e.g., compilation.IsSymbolAccessibleWithin(...) for the property/getter, potentially with throughType = concrete enum type) rather than only rejecting private.
| /// <see cref="StringComparison"/> to control key comparison. | ||
| /// </remarks> | ||
| [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] | ||
| public sealed class OptimizedEnumIndexAttribute : Attribute | ||
| { | ||
| /// <summary> | ||
| /// For <see cref="string"/> properties, specifies the comparison used when building | ||
| /// the lookup dictionary. Defaults to <see cref="StringComparison.Ordinal"/>. |
There was a problem hiding this comment.
The XML docs use <see cref="StringComparison"/> / <see cref="StringComparison.Ordinal"/> inside a type that also defines a StringComparison property, which can make the cref binding ambiguous or incorrect in generated documentation. Consider fully qualifying the enum type in the cref (e.g., global::System.StringComparison) to ensure the links resolve as intended.
| /// <see cref="StringComparison"/> to control key comparison. | |
| /// </remarks> | |
| [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] | |
| public sealed class OptimizedEnumIndexAttribute : Attribute | |
| { | |
| /// <summary> | |
| /// For <see cref="string"/> properties, specifies the comparison used when building | |
| /// the lookup dictionary. Defaults to <see cref="StringComparison.Ordinal"/>. | |
| /// <see cref="global::System.StringComparison"/> to control key comparison. | |
| /// </remarks> | |
| [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] | |
| public sealed class OptimizedEnumIndexAttribute : Attribute | |
| { | |
| /// <summary> | |
| /// For <see cref="string"/> properties, specifies the comparison used when building | |
| /// the lookup dictionary. Defaults to <see cref="global::System.StringComparison.Ordinal"/>. |
Summary
Adds
[OptimizedEnumIndex]— an attribute that can be placed on properties in intermediateOptimizedEnumbase classes to generate pre-builtFrom{PropertyName}/TryFrom{PropertyName}dictionary lookups on every concrete subclass. This closes the gap whereGetAll()is unavailable on the base class, making hand-written per-property lookups in every concrete type unnecessary.Also updates the local pack scripts to use a
-local.<n>pre-release suffix and bumpsVersionPrefixto1.2.0.Changes
New API
OptimizedEnumIndexAttribute— place on a base class property to opt it into index generationStringComparisonproperty on the attribute controls theStringComparerused forstringproperties (default:Ordinal); ignored for non-string typesGenerator
IndexedPropertyInfomodel record carrying property name, type, and comparer expressionEnumSyntaxProvider.CollectIndexedProperties— walks the base chain, validatesIEquatable<T>, resolves theStringComparerexpressionOptimizedEnum.scriban— emitss_by{PropertyName}dictionary andFrom{PropertyName}/TryFrom{PropertyName}methods for each indexed propertyOE0202(Warning) — emitted when[OptimizedEnumIndex]is applied to a property whose type does not implementIEquatable<T>Version / tooling
VersionPrefixbumped1.1.1→1.2.0pack-local.shandpack-local.ps1now produce{version}-local.{n}instead of{version}.{n}Validation
StringComparison, and OE0202 for a non-equatable property typeRelease Notes
v1.2.0 — New
[OptimizedEnumIndex]attribute. Decorate a property on an intermediate base class to generateFrom{PropertyName}andTryFrom{PropertyName}O(1) dictionary lookups on all concrete subclasses. String properties support a configurableStringComparison; all otherIEquatable<T>types are supported. Non-equatable types emit diagnosticOE0202.🤖 Generated with Claude Code