|
| 1 | +# Agents Guide |
| 2 | + |
| 3 | +See README.md for project overview and consumer API. |
| 4 | + |
| 5 | +## Build & Test |
| 6 | + |
| 7 | +```bash |
| 8 | +# Build |
| 9 | +dotnet build |
| 10 | + |
| 11 | +# Test |
| 12 | +dotnet test |
| 13 | + |
| 14 | +# Pack (multi-Roslyn-version NuGet package) |
| 15 | +./Pack.ps1 |
| 16 | +``` |
| 17 | + |
| 18 | +The project uses a `.slnx` solution file: `Zomp.SyncMethodGenerator.slnx`. |
| 19 | + |
| 20 | +## Multi-Roslyn-Version Architecture |
| 21 | + |
| 22 | +The generator ships multiple analyzer DLLs targeting different Roslyn versions so the same NuGet package works across .NET 8, 9, and 10 SDKs. |
| 23 | + |
| 24 | +- `Directory.Build.targets` — switches `Microsoft.CodeAnalysis.CSharp` version via `RoslynVersion` MSBuild property and defines `ROSLYN_X_Y_OR_GREATER` constants |
| 25 | +- `src/Zomp.SyncMethodGenerator/` — the generator project; `BaseOutputPath` is set per-variant so builds don't collide |
| 26 | +- `src/Zomp.SyncMethodGenerator.Pack/` — packing-only project that gathers pre-built variant DLLs into versioned `analyzers/dotnet/roslyn4.X/cs/` NuGet paths |
| 27 | +- `Pack.ps1` — builds all variants in parallel, then packs |
| 28 | + |
| 29 | +Roslyn variants: `roslyn4.8` (4.8.0, .NET 8), `roslyn4.12` (4.12.0, .NET 9), `roslyn5.0` (5.0.0, .NET 10). |
| 30 | + |
| 31 | +Use `#if ROSLYN_X_Y_OR_GREATER` guards for APIs that only exist in newer Roslyn versions (e.g., `ExtensionBlockDeclarationSyntax` is `ROSLYN_5_0_OR_GREATER` only). |
| 32 | + |
| 33 | +## Project Structure |
| 34 | + |
| 35 | +```text |
| 36 | +src/Zomp.SyncMethodGenerator/ Generator (netstandard2.0) |
| 37 | + SyncMethodSourceGenerator.cs Entry point — IIncrementalGenerator |
| 38 | + AsyncToSyncRewriter.cs Core transformation engine (CSharpSyntaxRewriter) |
| 39 | + SourceGenerationHelper.cs Output file structure and attribute definitions |
| 40 | + Extensions.cs Type-checking extensions on INamedTypeSymbol |
| 41 | + DiagnosticMessages.cs ZSMGEN001-003 diagnostic descriptors |
| 42 | + Models/ Data records for the pipeline |
| 43 | + Helpers/ EquatableArray<T>, DirectiveStack, etc. |
| 44 | + Properties/ Assembly attributes |
| 45 | + tools/ MSBuild props/targets shipped in the NuGet package |
| 46 | +src/Zomp.SyncMethodGenerator.Pack/ Packing-only project (no code) |
| 47 | +tests/Generator.Tests/ Unit tests (xUnit + Verify snapshot testing) |
| 48 | +tests/GenerationSandbox.Tests/ Integration tests (real-world patterns) |
| 49 | +``` |
| 50 | + |
| 51 | +## Transformation Pipeline |
| 52 | + |
| 53 | +1. **Find candidates** — `ForAttributeWithMetadataName` locates `[CreateSyncVersion]` on methods or types |
| 54 | +2. **Extract metadata** — parent class hierarchy, namespaces, configuration flags |
| 55 | +3. **Rewrite** — `AsyncToSyncRewriter` (a `CSharpSyntaxRewriter`) traverses the syntax tree: |
| 56 | + - Strips `async` modifier and `await` expressions |
| 57 | + - Transforms return types: `Task`/`ValueTask` to `void`, `Task<T>`/`ValueTask<T>` to `T` |
| 58 | + - Transforms collection types: `IAsyncEnumerable<T>` to `IEnumerable<T>` |
| 59 | + - Transforms memory types: `Memory<T>` to `Span<T>` (except in arrays) |
| 60 | + - Removes `CancellationToken` and `IProgress<T>` parameters (configurable) |
| 61 | + - Renames method calls: strips `Async` suffix |
| 62 | + - Handles special methods: `Task.FromResult(x)` to `x`, `Task.Delay()` to `Thread.Sleep()` |
| 63 | + - Processes `#if SYNC_ONLY` / `#if !SYNC_ONLY` directives |
| 64 | +4. **Emit** — `SourceGenerationHelper` wraps the rewritten method in namespace/class structure |
| 65 | + |
| 66 | +## Testing Conventions |
| 67 | + |
| 68 | +- **Snapshot testing** with Verify.SourceGenerators — test inputs are inline C# strings, outputs are `.verified.cs` files in `Snapshots/` |
| 69 | +- Test pattern: `[Fact] public Task TestName() => "source code".Verify();` |
| 70 | +- Snapshot files: `{TestClass}.{TestName}[.Platform].g.verified.cs` |
| 71 | +- Tests compile against real framework assemblies via `TestHelper` |
| 72 | + |
| 73 | +## Key Conventions |
| 74 | + |
| 75 | +- Central package management (`Directory.Packages.props`) — never put versions in csproj files |
| 76 | +- `TreatWarningsAsErrors` is enabled globally |
| 77 | +- StyleCop + NetAnalyzers enforced; `.editorconfig` defines style rules |
| 78 | +- File-scoped namespaces required |
| 79 | +- Nerdbank.GitVersioning for version management (from git tags/height) |
| 80 | +- The generator targets `netstandard2.0` for maximum host compatibility |
| 81 | +- `EquatableArray<T>` wraps `ImmutableArray<T>` for value equality in the incremental pipeline |
| 82 | + |
| 83 | +## Diagnostics |
| 84 | + |
| 85 | +| ID | Description | |
| 86 | +|----|-------------| |
| 87 | +| ZSMGEN001 | Invalid nesting of `SYNC_ONLY` directive | |
| 88 | +| ZSMGEN002 | `SYNC_ONLY` mixed with other symbols in `#if` condition | |
| 89 | +| ZSMGEN003 | `SYNC_ONLY` used with `#elif` | |
0 commit comments