feat: union-typed setup and verify overloads for C# 15 - #856
Conversation
Moves global.json to 11.0.100-preview.7 and adds net11.0 to the runtime and to the three multi-target test projects; the generator and analyzer test projects, which pin a single target, move from net10.0 to net11.0. On net11.0 only, the test projects compile with the preview language version and set MockolateUnionParameters, so that the upcoming union-typed setup surface is exercised there while every other target keeps today's behaviour. Consumers are unaffected: the shipped assembly still compiles as C# 14 and the README keeps recommending the .NET 10 SDK. - Pipeline: new unlisted UnionTests target running the test projects on net11.0. - CI: every setup-dotnet step that runs the Nuke build (build.yml, ci.yml, ci-analysis.yml) pins the SDK via global.json; the package push step keeps the runner SDK since it only runs dotnet nuget push. Dedicated union-tests job in build.yml and ci.yml, awaited by publish-test-results. - API snapshot for net11.0 (identical to net10.0 apart from the header). - HttpClient generator snapshot: .NET 11 marks Send(HttpRequestMessage, CancellationToken) as unsupported on android/ios/tvos, which the generated mock now mirrors. - Benchmarks project moves to net11.0; target lists in README, docs index, CLAUDE.md and copilot-instructions mention .NET 11.
Adds the opt-in switch and the building block for union-typed setup and
verify parameters, without changing any generated mock yet.
- The generator reads the effective language version and the
MockolateUnionParameters build property. The property wins when set
("true" opts in on a preview compiler, anything else is the kill
switch); otherwise unions are used once the host Roslyn ships C# 15 and
the compilation targets it. Below that, nothing is emitted.
- When enabled, a ParameterArg.g.cs is generated into the consuming
assembly: a [Union] struct with the case types IParameter<T> and T?,
stored in typed slots (no boxing), with TryGetValue accessors and a
ToParameterMatch() bridge to IParameterMatch<T>. UnionAttribute is
declared in that file only when neither the framework (it ships with
.NET 11) nor the consuming assembly already declares it, so net48,
netstandard2.0 and net8.0 consumers and PolySharp-style polyfills work.
- build/Mockolate.props (packed as build and buildTransitive) makes the
property visible to the generator; Tests/Directory.Build.props imports
it so project references behave like the package.
- Generator tests cover the detection matrix, the polyfill decision and
the emitted text. The generator test project pins the minimum Roslyn,
which predates unions, so the union conversions themselves are tested
in Mockolate.Tests on net11.0 with the SDK compiler.
When union support is detected, every method whose name is unique on the mocked type and that has value-capable parameters gets one Setup and one Verify overload per assignment of its parameters to either ParameterArg<T>? (an It matcher or a literal value; null and default stand for the literal default) or Func<T, bool> (a predicate forwarded to It.Satisfies together with the caller's argument text). The count stays at 2^n for n eligible parameters, the same as today's matcher/value set, so predicates come for free. Without union support the generator output is unchanged. Rules that keep the surface sound: - Overloaded method groups (including a generic sibling) keep the classic set: a union conversion loses to the identity and numeric conversions of a sibling, so Setup.M(5) on M(int)/M(long) would be ambiguous. - Delegate-typed parameters offer the raw delegate instead of a predicate, because a lambda never converts to a union type. - ref/out/in and Span parameters keep their matcher slot. - Generic methods, params methods and ref-struct pipelines keep the classic overloads: a union slot hides the type argument from type inference, and a params slot cannot survive inside a union type. - Above four parameters only the all-union overload is emitted. - Priorities mirror the classic set (all-union int.MaxValue, object-typed slots below IParameters, predicate combinations by union count), so null and default arguments still bind to the union overload. - All-literal calls keep the WithLiteralValues / literal VerifyMethod fast paths at runtime; everything else uses ToParameterMatch(). ParameterArg.g.cs now also polyfills OverloadResolutionPriorityAttribute and CallerArgumentExpressionAttribute where the framework lacks them, and in union mode every overload set of the compilation carries priorities, so union mode works the same on net48, netstandard2.0 and net8.0. Generators cannot see each other's output, so a project that gets those two attributes from PolySharp sets MockolateUnionAttributePolyfills=false (or lists the attribute names it provides itself); UnionAttribute keeps following the compilation. Tests: union-mode snapshot scenarios (classic scenarios pinned to C# 14 so they never flip), generator tests for the overload shapes and the classic fallbacks, and Mockolate.Tests on net11.0, where the whole existing suite now runs against the union overloads plus dedicated tests for predicates, null, default, delegate-typed, optional, object and out parameters, overloaded groups, scenarios, verify AnyParameters and a delegate mock.
Extends union mode to indexers: every indexer that is the only one of its key count on the type and has value-capable keys gets one Setup and one Verify indexer per assignment of its keys to ParameterArg<T>? or Func<T, bool>, with the same slot rules as methods (delegate-typed keys offer the raw delegate and dispatch as literal values, Span keys keep their matcher slot, ref-struct and params keys keep the classic set). Indexers of the same arity keep the classic set, because two indexers with convertible key types (this[int]/this[long]) would make Setup[5] ambiguous; indexers of different arity never compete. Indexers have no IParameters overload, so the all-union indexer takes the classic all-matcher priority (int.MaxValue) and predicate combinations rank by their union slot count. Keys always dispatch through ToParameterMatch() because IndexerSetup has no literal fast path; the typed verify path is used up to four keys, the IndexerGetterAccess/IndexerSetterAccess predicate above that. Without union support the output is unchanged. Tests: a new IUnionIndexers coverage interface with one indexer per key count (getter/setter, getter-only, setter-only, delegate-typed key, five keys) and its union snapshot, generator assertions for the two-key indexer overloads, the delegate-typed key and same-arity indexers falling back to the classic set, and net11.0 behavioural tests for indexer setup and verify with predicates, literals and matchers, the setter verification, the failure message and overloaded indexers.
UseVerificationAnalyzer only recognised a return type named VerificationResult, so verifications returning the nested VerificationResult<T>.IgnoreParameters were never reported when left unused. That shape is returned by the classic all-value overload (Verify.Method(42)), by the parameterless overload (Verify.Method()) and, since union mode, by every union-typed verify overload, which had silently disabled Mockolate0001 for that surface. The analyzer now unwraps the nested type; the code fixer needs no change because the count assertions are extension members on VerificationResult<T>. Classic-mode consumers with an unused Verify.Method(42) or Verify.Method() statement get Mockolate0001 from now on, which is what the rule is for (such a statement asserts nothing).
- A "Union Parameters (C# 15)" section in the parameter matching page: how union mode is enabled, predicates without It.Satisfies, the null/default fallback to the declared default, which parameters keep a matcher slot and which members keep the classic overloads, and the MockolateUnionAttributePolyfills switch. Short notes on the methods and indexers pages, a README feature bullet with a link, and a CLAUDE.md note on the generator's union mode. - Benchmarks/Mockolate.Benchmarks.Unions compiles the CompleteMethod workflow and a setup-only variant in union mode (literal, matcher, predicate) with the same job as the classic Mockolate.Benchmarks, for a job-for-job comparison. Union mode is a compilation-wide switch, so it cannot share a project with the classic benchmarks; it is part of the solution build but not of the CI benchmark matrix, so CI compiles it and never runs it. Measured setup-only: literal 91 ns / 584 B (same allocation as the classic value path, so ParameterArg<T> does not box), matcher 98 ns / 592 B, predicate 88 ns / 632 B.
There was a problem hiding this comment.
🟡 Changes recommended
The Sources.ParameterArg XML doc <see ... /> markup currently disagrees with the checked-in expected snapshots, which is likely to cause snapshot/test failures unless aligned.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a C# 15 “union mode” to Mockolate’s generated setup/verify surface, enabling predicate arguments without exploding overload counts, and updates the repo/tooling to validate parity via an additional net11.0 union-mode test run.
Changes:
- Generate union-typed
ParameterArg<T>?/ predicate overloads when C# 15 unions are available orMockolateUnionParameters=true, including attribute polyfills when needed. - Add net11.0 union-mode test coverage (runtime + generator snapshots) and CI/Nuke targets to run the suite twice (classic + union mode).
- Update docs/readme and add benchmark project to measure union-mode overhead.
File summaries
| File | Description |
|---|---|
| Tests/Mockolate.Tests/UnionSetupTests.cs | Adds runtime tests covering union-mode setup/verify binding and predicate behavior. |
| Tests/Mockolate.Tests/ParameterArgTests.cs | Adds runtime tests for ParameterArg<T> union conversion semantics on net11.0. |
| Tests/Mockolate.Tests/GeneratorCoverage/IUnionIndexers.cs | Adds union-mode indexer shapes for snapshot coverage. |
| Tests/Mockolate.SourceGenerators.Tests/UnionParameterArgTests.cs | Validates ParameterArg emission/polyfill behavior under minimum Roslyn. |
| Tests/Mockolate.SourceGenerators.Tests/UnionOverloadTests.cs | Validates union-mode overload shapes and generated implementation patterns. |
| Tests/Mockolate.SourceGenerators.Tests/TestHelpers/TestAnalyzerConfigOptionsProvider.cs | Adds test helper for passing global analyzer config options to the generator. |
| Tests/Mockolate.SourceGenerators.Tests/TestHelpers/Generator.cs | Extends test driver to vary language version and pass global options into the generator driver. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/SnapshotScenario.cs | Adds UnionMode flag to snapshot scenario definition. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/MockGenerationSnapshotTests.cs | Adds union-mode snapshot scenarios and pins classic vs union generation paths. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/UnionIndexers_CanBeCreated_Unions/ParameterArg.g.cs | New expected union-mode ParameterArg<T> output snapshot. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/UnionIndexers_CanBeCreated_Unions/MockBehaviorExtensions.g.cs | New expected union-mode extensions snapshot for UnionIndexers scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/UnionIndexers_CanBeCreated_Unions/Mock.g.cs | New expected union-mode base Mock snapshot for UnionIndexers scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated_Unions/ParameterArg.g.cs | New expected union-mode ParameterArg<T> output snapshot. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated_Unions/MockBehaviorExtensions.g.cs | New expected union-mode extensions snapshot for KeywordEdgeCases scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/KeywordEdgeCases_CanBeCreated_Unions/Mock.g.cs | New expected union-mode base Mock snapshot for KeywordEdgeCases scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/HttpClient_CanBeCreated/Mock.HttpClient.g.cs | Updates expected snapshot (platform attributes) for classic HttpClient scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/HttpClient_CanBeCreated_Unions/ParameterArg.g.cs | New expected union-mode ParameterArg<T> output snapshot. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/HttpClient_CanBeCreated_Unions/MockBehaviorExtensions.g.cs | New expected union-mode extensions snapshot for HttpClient scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/HttpClient_CanBeCreated_Unions/Mock.g.cs | New expected union-mode base Mock snapshot for HttpClient scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated_Unions/ParameterArg.g.cs | New expected union-mode ParameterArg<T> output snapshot. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated_Unions/MockBehaviorExtensions.g.cs | New expected union-mode extensions snapshot for ComprehensiveInterface scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated_Unions/Mock.g.cs | New expected union-mode base Mock snapshot for ComprehensiveInterface scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveInterface_CanBeCreated_Unions/ActionFunc.g.cs | New expected union-mode snapshot for generated Action/Func polyfills. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveDelegate_CanBeCreated_Unions/ReturnsThrowsAsyncExtensions.g.cs | New expected union-mode snapshot for async returns/throws extensions. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveDelegate_CanBeCreated_Unions/ParameterArg.g.cs | New expected union-mode ParameterArg<T> output snapshot. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveDelegate_CanBeCreated_Unions/MockBehaviorExtensions.g.cs | New expected union-mode extensions snapshot for ComprehensiveDelegate scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Snapshot/Expected/ComprehensiveDelegate_CanBeCreated_Unions/Mock.g.cs | New expected union-mode base Mock snapshot for ComprehensiveDelegate scenario. |
| Tests/Mockolate.SourceGenerators.Tests/Mockolate.SourceGenerators.Tests.csproj | Moves generator tests to net11.0. |
| Tests/Mockolate.Analyzers.Tests/UseVerificationAnalyzerTests.cs | Adds analyzer coverage for unused IgnoreParameters verification results. |
| Tests/Mockolate.Analyzers.Tests/Mockolate.Analyzers.Tests.csproj | Moves analyzer tests to net11.0. |
| Tests/Directory.Build.props | Adds net11.0 to test TFMs and enables preview + union mode for net11.0 test runs. |
| Source/Mockolate/Mockolate.csproj | Packs build/Mockolate.props into NuGet (build/buildTransitive). |
| Source/Mockolate/build/Mockolate.props | Exposes compiler-visible properties for union mode and polyfill coordination. |
| Source/Mockolate.SourceGenerators/Sources/Sources.ParameterArg.cs | Adds ParameterArg<T> source template with optional attribute polyfills. |
| Source/Mockolate.SourceGenerators/Sources/Sources.MockDelegate.cs | Extends delegate mock generation to support union-mode overload emission. |
| Source/Mockolate.SourceGenerators/Sources/Sources.MockCombination.cs | Threads union-mode flag through combination mock generation pipeline. |
| Source/Mockolate.SourceGenerators/MockGenerator.cs | Detects union capability/options, emits ParameterArg.g.cs, and toggles union-mode overloads. |
| Source/Mockolate.SourceGenerators/Entities/Type.cs | Tracks delegate-typed parameters to preserve “lambda as value” semantics. |
| Source/Mockolate.Analyzers/UseVerificationAnalyzer.cs | Expands analyzer matching to include nested VerificationResult<T>.IgnoreParameters. |
| Source/Directory.Build.props | Adds net11.0 to source project TFMs. |
| README.md | Updates supported TFMs and documents C# 15 predicate support at a high level. |
| Pipeline/Build.UnitTest.cs | Adds UnionTests Nuke target to run net11.0 union-mode test suite. |
| Mockolate.slnx | Adds the union benchmarks project to the solution. |
| global.json | Pins SDK to .NET 11 preview. |
| Docs/pages/setup/04-parameter-matching.md | Documents union parameters behavior, activation, and polyfill coordination. |
| Docs/pages/setup/03-indexers.md | Documents union parameter behavior for indexers. |
| Docs/pages/setup/02-methods.md | Notes predicate support via union parameters. |
| Docs/pages/00-index.md | Updates supported TFMs list in docs index. |
| CLAUDE.md | Updates supported TFMs and adds union-mode generator notes. |
| Benchmarks/Mockolate.Benchmarks/Mockolate.Benchmarks.csproj | Moves benchmarks to net11.0. |
| Benchmarks/Mockolate.Benchmarks.Unions/UnionSetupBenchmarks.cs | Adds micro-benchmark for union-mode setup argument binding. |
| Benchmarks/Mockolate.Benchmarks.Unions/UnionParameterBenchmarks.cs | Adds end-to-end benchmark (setup/invoke/verify) for union mode. |
| Benchmarks/Mockolate.Benchmarks.Unions/Program.cs | Adds BenchmarkDotNet entry point for union benchmarks. |
| Benchmarks/Mockolate.Benchmarks.Unions/Mockolate.Benchmarks.Unions.csproj | Adds union benchmarks project with preview + MockolateUnionParameters=true. |
| Benchmarks/Mockolate.Benchmarks.Unions/IMyMethodInterface.cs | Defines benchmark interface used for union-mode benchmarks. |
| Benchmarks/Mockolate.Benchmarks.Unions/BenchmarksBase.cs | Adds baseline BenchmarkDotNet config for union-mode benchmarks. |
| .github/workflows/ci.yml | Adds a dedicated “union tests” job and ensures global.json is honored. |
| .github/workflows/ci-analysis.yml | Ensures global.json is honored for analysis workflow jobs. |
| .github/workflows/build.yml | Adds a dedicated “union tests” job and ensures global.json is honored. |
| .github/copilot-instructions.md | Updates supported TFMs list to include net11.0. |
Review details
Suppressed comments (1)
Source/Mockolate.SourceGenerators/Sources/Sources.ParameterArg.cs:177
- This
<see ... />reference toIParameterMatch<T>is self-closing, but the expected generated snapshots use the form with explicit display text (<see ...>IParameterMatch<T></see>). Aligning the template avoids snapshot drift and produces clearer XML docs.
/// <summary>
/// The <see cref="global::Mockolate.Parameters.IParameterMatch{T}" /> for this argument: the matcher itself,
/// or an equality match for the literal value.
- Files reviewed: 68/74 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Test Results 34 files + 10 34 suites +10 18m 29s ⏱️ + 6m 4s Results for commit bee4f40. ± Comparison against base commit 81e17dd. This pull request removes 15 and adds 3637 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
Classify Mockolate.Benchmarks.Unions as a test project for SonarCloud, matching the classic benchmark project (which is only classified as test code because it references mocking frameworks), so its lines are neither analysed as production code nor counted as uncovered new code. Split the nested ternary in GenerateUnionSlotCombinations (S3358).
There was a problem hiding this comment.
🟡 Changes recommended
The NuGet packing entry for build/Mockolate.props uses PackagePath="build;buildTransitive", which is unlikely to place the props in both intended locations and can break generator option visibility for consumers.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 69/75 changed files
- Comments generated: 1
- Review effort level: Lite
🚀 Benchmark ResultsDetails
Details
Details
Details
Details
Details
|
The .NET 11 SDK writes a runtimeconfig.dev.json with hot reload options but without additionalProbingPaths for Debug builds. VsTest 18.0.1, bundled with Stryker 4.10.0, reads that file on Linux (where no testhost.exe exists next to the test assembly) and throws a NullReferenceException in DotnetTestHostManager.GetTestHostPath, so test discovery is aborted and mutation testing fails with "No test result reported". Reproduced and verified in a Debian WSL environment with the same SDK and Stryker version: 0 tests discovered before, 3770 after.
There was a problem hiding this comment.
🔵 Needs a closer look
The NuGet packing path for Mockolate.props appears incorrect and the new union-mode test target set does not match the PR’s stated “whole test suite parity” intent.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
Pipeline/Build.UnitTest.cs:61
- The PR description says the whole test suite is run a second time in union mode as a parity proof, but
UnionTestProjectsomitsMockolate_Analyzers_Tests(and alsoBuild_Tests). That means analyzer changes (including the new IgnoreParameters usage rule) won’t be exercised under the net11.0/union configuration.
Source/Mockolate/Mockolate.csproj:10
PackagePathis set tobuild;buildTransitive, which NuGet/MSBuild will treat as a literal folder name (including the semicolon), not as “pack to both locations”. This likely placesMockolate.propsunder an unexpected path in the nupkg, preventing it from being imported and makingMockolateUnionParametersnon-compiler-visible for consumers.
<ProjectReference Include="..\Mockolate.Analyzers.CodeFixers\Mockolate.Analyzers.CodeFixers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<None Include="$(MSBuildProjectDirectory)\..\Mockolate.Analyzers.CodeFixers\bin\$(Configuration)\netstandard2.0\Mockolate.Analyzers.CodeFixers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
<None Include="$(MSBuildProjectDirectory)\..\Mockolate.SourceGenerators\bin\$(Configuration)\netstandard2.0\Mockolate.SourceGenerators.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
<None Include="build\Mockolate.props" Pack="true" PackagePath="build;buildTransitive"/>
</ItemGroup>
- Files reviewed: 69/75 changed files
- Comments generated: 0 new
- Review effort level: Lite
- Delegate mocks: emit [OverloadResolutionPriority] on the IParameters setup/verify overloads so they no longer lose to the union overloads - Methods with more than four parameters keep a raw-delegate value overload so lambda arguments still compile in union mode - Treat System.Delegate/System.MulticastDelegate parameters as delegates (raw value slot instead of a predicate) - Union indexer keys resolve null/default to the declared parameter default, like the method path - Scope the union-eligibility uniqueness checks per member scope and evaluate them lazily (never when union mode is off) - Accept suffix-less attribute names in MockolateUnionAttributePolyfills - Detect C# 15 by enum name instead of the hardcoded value 1500 - Pin Mockolate.Benchmarks to classic mode so the classic-vs-union benchmark comparison survives the C# 15 GA - Share one CovariantParameterAdapter<T> (emitted in Mock.g.cs), the slow-path verify condition, and the summary cref formatting between classic and union emission; hoist union indexer key matchers out of the per-interaction lambdas; use MaxExplicitParameters consistently
78d9b3b to
b66af9e
Compare
There was a problem hiding this comment.
🟡 Changes recommended
There are at least two concrete correctness/packaging issues in the changed code (ParameterArg’s boxed Value behavior and NuGet PackagePath usage) that should be fixed before merge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
Source/Mockolate/Mockolate.csproj:9
PackagePath="build;buildTransitive"is unlikely to produce a valid NuGet layout;PackagePathis a single path, so this may end up packing the props file into a literalbuild;buildTransitivefolder (or otherwise not importing correctly). If you intend to ship the same props in bothbuild/andbuildTransitive/, it needs two<None .../>items (one per target path).
- Files reviewed: 87/93 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The nupkg packing metadata for Source/Mockolate/build/Mockolate.props appears incorrect (PackagePath="build;buildTransitive"), which risks breaking automatic import of the props and therefore union-mode configuration for consumers.
Review details
Suppressed comments (1)
Source/Mockolate/Mockolate.csproj:9
PackagePath="build;buildTransitive"will likely be treated as a single literal folder name in the nupkg ("build;buildTransitive/") rather than placing the props into bothbuild/andbuildTransitive/. That would prevent consuming projects from automatically importingMockolate.props, so the generator may not seeMockolateUnionParametersas compiler-visible.
- Files reviewed: 87/93 changed files
- Comments generated: 0 new
- Review effort level: Lite
Mockolate.Benchmarks.Unions was a local classic-vs-union measurement rig; it is not wired into the Benchmarks pipeline and would only rot. The classic pin on Mockolate.Benchmarks stays so results remain comparable to the CI baseline once the SDK defaults to C# 15.
- Store files identical across scenarios once under Expected/_Shared, referenced by a _shared.txt manifest per scenario (~29% fewer lines) - Drop the HttpClient union scenario; its shapes are covered by the comprehensive interface scenario - Mark the Expected snapshots as linguist-generated so GitHub collapses them in pull request diffs
There was a problem hiding this comment.
🟡 Changes recommended
There are at least two concrete correctness issues (a missing using Mockolate; causing a test compile failure and a likely incorrect NuGet PackagePath that prevents Mockolate.props from landing in both build import locations).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
Source/Mockolate/Mockolate.csproj:10
PackagePath="build;buildTransitive"is likely treated as a literal folder name during packing, so Mockolate.props won’t be placed into bothbuild/andbuildTransitive/as intended. Duplicate the item with two separate PackagePath values.
<None Include="$(MSBuildProjectDirectory)\..\Mockolate.SourceGenerators\bin\$(Configuration)\netstandard2.0\Mockolate.SourceGenerators.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
<None Include="build\Mockolate.props" Pack="true" PackagePath="build;buildTransitive"/>
</ItemGroup>
- Files reviewed: 81/92 changed files
- Comments generated: 3
- Review effort level: Lite
The default instance is the literal case everywhere else (IsLiteral, Literal, ToParameterMatch); Value returned null instead of the boxed default(T) for value types.
The coverlet VSTest collector cannot reliably read the coverage hits of xunit.v3 out-of-process runs (intermittent EndOfStreamException at session end), which silently dropped the whole Mockolate.SourceGenerators coverage from the SonarCloud quality gate. xunit v2 runs in-process in the testhost, where the collector works (reproduced and verified locally in Release). The snapshot acceptance test is gated by the MOCKOLATE_ACCEPT_SNAPSHOTS environment variable instead of the v3-only Explicit flag, and the snapshot test helpers use tabs like the rest of the repository.
There was a problem hiding this comment.
🔵 Needs a closer look
The NuGet packing metadata for build/Mockolate.props uses PackagePath="build;buildTransitive", which likely won’t place the props file into both build/ and buildTransitive/ as intended.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
Tests/Mockolate.SourceGenerators.Tests/Snapshot/MockGenerationSnapshotTests.cs:188
- Indentation in this block uses spaces, but the repo
.editorconfigrequires tabs for*.cs(indent_style = tab). Reindenting this file to tabs would keep formatting consistent and avoid style-only churn in future diffs.
Source/Mockolate/Mockolate.csproj:10
PackagePathcontains a semicolon (build;buildTransitive), which will be treated as a literal path segment during packing rather than placing the file into bothbuild/andbuildTransitive/. This likely prevents consumers (or transitive consumers) from importingMockolate.propsas intended.
<None Include="$(MSBuildProjectDirectory)\..\Mockolate.Analyzers.CodeFixers\bin\$(Configuration)\netstandard2.0\Mockolate.Analyzers.CodeFixers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
<None Include="$(MSBuildProjectDirectory)\..\Mockolate.SourceGenerators\bin\$(Configuration)\netstandard2.0\Mockolate.SourceGenerators.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
<None Include="build\Mockolate.props" Pack="true" PackagePath="build;buildTransitive"/>
</ItemGroup>
- Files reviewed: 82/93 changed files
- Comments generated: 0 new
- Review effort level: Lite
|



Predicate lambdas were never offered as setup/verify arguments because every parameter would need a value, matcher and predicate overload, so the overload count grows with 3^n. C# 15 unions collapse value and matcher into one
ParameterArg<T>?parameter, which makes a predicate overload per parameter affordable (2^n) without boxing:Union mode is used when the consumer compiles with C# 15 or sets
MockolateUnionParameters=true; attribute polyfills are generated so it also works on older target frameworks. Below C# 15 the generated output is unchanged.The repository moves to the .NET 11 preview SDK, adds
net11.0targets, and runs the whole test suite a second time in union mode as the parity proof.Classic-mode change worth a release note: Mockolate0001 now also flags unused all-value and parameterless verifications, which return
IgnoreParameters.