|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidance to AI coding agents when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +NetEscapades.EnumGenerators is a C# source generator that creates high-performance extension methods for enums, replacing slow reflection-based `Enum.ToString()`, `Enum.IsDefined()`, `Enum.Parse()`, etc. with compile-time generated switch-based alternatives. It also includes Roslyn analyzers (with code fixes) that suggest using the generated methods, and an interceptor generator that automatically replaces standard enum method calls. |
| 8 | + |
| 9 | +## Build Commands |
| 10 | + |
| 11 | +This project uses [Nuke](https://nuke.build/) for builds. The solution file is `NetEscapades.EnumGenerators.sln`. |
| 12 | + |
| 13 | +```bash |
| 14 | +# Build |
| 15 | +./build.cmd Compile # Windows |
| 16 | +./build.sh Compile # Unix |
| 17 | + |
| 18 | +# Run all tests (unit + integration) |
| 19 | +./build.cmd Test |
| 20 | +./build.sh Test |
| 21 | + |
| 22 | +# Run a single test project directly |
| 23 | +dotnet test tests/NetEscapades.EnumGenerators.Tests |
| 24 | + |
| 25 | +# Run a single test by name |
| 26 | +dotnet test tests/NetEscapades.EnumGenerators.Tests --filter "FullyQualifiedName~CanGenerateEnumExtensionsInGlobalNamespace" |
| 27 | + |
| 28 | +# Pack NuGet packages |
| 29 | +./build.cmd Pack |
| 30 | + |
| 31 | +# Full CI pipeline (clean + test + package tests) |
| 32 | +./build.cmd Clean Test TestPackage |
| 33 | +``` |
| 34 | + |
| 35 | +## Architecture |
| 36 | + |
| 37 | +### NuGet Package Structure (6 packages) |
| 38 | + |
| 39 | +The project ships as multiple packages to avoid forcing runtime dependencies: |
| 40 | + |
| 41 | +- **NetEscapades.EnumGenerators** — meta-package referencing Generators + RuntimeDependencies |
| 42 | +- **NetEscapades.EnumGenerators.Generators** — the source generator + analyzers (netstandard2.0) |
| 43 | +- **NetEscapades.EnumGenerators.Attributes** — `[EnumExtensions]` attribute (netstandard2.0, net451) |
| 44 | +- **NetEscapades.EnumGenerators.RuntimeDependencies** — `EnumParseOptions`, `SerializationOptions`, `SerializationTransform` types (netstandard2.0, net451) |
| 45 | +- **NetEscapades.EnumGenerators.Interceptors** — C# 12+ interceptor generator (netstandard2.0) |
| 46 | +- **NetEscapades.EnumGenerators.Interceptors.Attributes** — `[Interceptable<T>]` attribute |
| 47 | + |
| 48 | +### Source Generator Pipeline (`src/NetEscapades.EnumGenerators.Generators/`) |
| 49 | + |
| 50 | +- **EnumGenerator.cs** — `IIncrementalGenerator` entry point. Uses `ForAttributeWithMetadataName` to find enums decorated with `[EnumExtensions]` or `[EnumExtensions<T>]`. Extracts `EnumToGenerate` model from syntax/semantic analysis. |
| 51 | +- **SourceGenerationHelper.cs** (~2300 lines) — Generates the extension class source code via `StringBuilder`. Produces `ToStringFast()`, `IsDefined()`, `TryParse()`, `Parse()`, `GetValues()`, `GetNames()`, `HasFlagFast()`, etc. Handles multiple code paths: with/without System.Memory, with/without runtime dependencies, C# 14 field keyword support, collection expressions. |
| 52 | +- **EnumToGenerate.cs** — Immutable model capturing everything needed to generate code for one enum (name, namespace, underlying type, members, flags, metadata source). Must implement `IEquatable<T>` correctly for incremental caching. |
| 53 | +- **TrackingNames.cs** — String constants for incremental generator step tracking, used in tests to verify caching behavior. |
| 54 | + |
| 55 | +### Analyzers (`src/NetEscapades.EnumGenerators.Generators/Diagnostics/`) |
| 56 | + |
| 57 | +Two categories: |
| 58 | + |
| 59 | +**Definition Analyzers** (always on) — validate `[EnumExtensions]` usage: |
| 60 | +- `DuplicateEnumValueAnalyzer` — warns about duplicate enum values |
| 61 | +- `DuplicateExtensionClassAnalyzer` — warns about conflicting extension class names |
| 62 | +- `EnumInGenericTypeAnalyzer` — warns about enums nested in generic types |
| 63 | + |
| 64 | +**Usage Analyzers** (NEEG004–NEEG012, opt-in via `EnumGenerator_EnableUsageAnalyzers`) — suggest using generated methods instead of framework methods: |
| 65 | +- Each has a paired `CodeFixProvider` for automatic fixes |
| 66 | +- Controlled by `build_property.EnumGenerator_EnableUsageAnalyzers` in `.editorconfig` |
| 67 | +- Pattern: analyzer detects `Enum.Method()` call on a decorated enum, code fix replaces with `EnumExtensions.MethodFast()` |
| 68 | + |
| 69 | +### Interceptors (`src/NetEscapades.EnumGenerators.Interceptors/`) |
| 70 | + |
| 71 | +- **InterceptorGenerator.cs** — C# 12+ feature. Intercepts `ToString()` and `HasFlag()` calls at compile time and replaces them with generated fast versions. |
| 72 | +- Requires .NET 8.0.400+ SDK, controlled by `EnumGenerator_EnableInterception` MSBuild property. |
| 73 | + |
| 74 | +### Key Design Constraints |
| 75 | + |
| 76 | +- **Generators target netstandard2.0** — must run inside any version of the Roslyn compiler. Cannot use modern .NET APIs directly; uses `Polyfill` package for compatibility. |
| 77 | +- **Incremental generator caching** — `EnumToGenerate` and all pipeline outputs must correctly implement equality. Tests verify this by running the generator twice and asserting all outputs are `Cached` or `Unchanged` on the second run. |
| 78 | +- **No Roslyn symbols in outputs** — the generator must not store `ISymbol`, `Compilation`, or `SyntaxNode` objects in pipeline outputs (would break caching). `TestHelpers.AssertObjectGraph` validates this. |
| 79 | + |
| 80 | +## Testing |
| 81 | + |
| 82 | +### Test Projects |
| 83 | + |
| 84 | +- **NetEscapades.EnumGenerators.Tests** — main test project with: |
| 85 | + - **Snapshot tests** (Verify.Xunit) for generated source output, stored in `tests/.../Snapshots/`. Use `dotnet tool run dotnet-verify accept` or manually update `.verified.` files when output intentionally changes. |
| 86 | + - **Generator tests** (`EnumGeneratorTests.cs`) — feed source code strings through `TestHelpers.GetGeneratedOutput()`, assert no diagnostics, verify output with Verify snapshots. |
| 87 | + - **Analyzer tests** — use `Microsoft.CodeAnalysis.Testing` framework. Each inherits `AnalyzerTestsBase<TAnalyzer, TCodeFixer>` providing `VerifyAnalyzerAsync`/`VerifyCodeFixAsync` helpers. |
| 88 | + - **Incremental caching tests** — every generator test automatically runs twice to verify caching correctness. |
| 89 | +- **Integration test projects** — test the actual NuGet packages work correctly in various configurations (NetStandard, System.Memory, Interceptors, PrivateAssets). |
| 90 | +- **Benchmarks** — BenchmarkDotNet project comparing generated methods vs. framework methods. |
| 91 | + |
| 92 | +### Test Patterns |
| 93 | + |
| 94 | +- Generator tests use `TestHelpers.Options` record to configure source input, language version, analyzer options, and features. |
| 95 | +- Snapshot files use parameter-based naming: `TestName_param1_param2.verified.txt`. |
| 96 | +- Version strings in generated code are scrubbed via `ScrubExpectedChanges()` to avoid snapshot churn on version bumps. |
| 97 | + |
| 98 | +## Version Management |
| 99 | + |
| 100 | +- Version is defined in `version.props` (`VersionPrefix` + `VersionSuffix`). |
| 101 | +- `Constants.cs` in the generators project also contains the version string used in `[GeneratedCode]` attributes. |
| 102 | +- Both must be updated together when changing versions. |
| 103 | + |
| 104 | +## MSBuild Properties (user-facing configuration) |
| 105 | + |
| 106 | +- `EnumGenerator_EnumMetadataSource` — default metadata source attribute (EnumMemberAttribute, DisplayAttribute, DescriptionAttribute, None) |
| 107 | +- `EnumGenerator_ForceInternal` — force all generated extension classes to be internal |
| 108 | +- `EnumGenerator_EnableUsageAnalyzers` — enable usage analyzers (default: false) |
| 109 | +- `EnableEnumGeneratorInterceptor` — enable interceptor generator |
0 commit comments