fix(assertions): gate IsEqualTo<TValue, TOther> overload to net9+ (#5765)#5767
fix(assertions): gate IsEqualTo<TValue, TOther> overload to net9+ (#5765)#5767
Conversation
) The IsEqualTo<TValue, TOther> / IsNotEqualTo<TValue, TOther> overloads added in 1.40.0 (#5751) rely on [OverloadResolutionPriority(-1)] to lose to the source-generated single-generic overload when both apply. That attribute is only honored by C# 13+ and only present in System.Runtime.CompilerServices on .NET 9+. On the net8.0 / netstandard2.0 builds of TUnit.Assertions, Polyfill silently drops the attribute and every same-type IsEqualTo call becomes ambiguous (CS0121), breaking effectively every existing test suite. Gating the new overloads behind #if NET9_0_OR_GREATER means net8.0 / netstandard2.0 consumers fall back to the original well-defined overload with no contest. net9.0+ consumers using a modern SDK keep the wrapper Value Object support introduced in #5720. Public API snapshots for net8.0 and netstandard2.0 (Net4_7) updated to drop the gated overloads. Issue5720Tests gated to match. Added Issue5765 regression test covering same-type IsEqualTo across enum/primitive/record.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 4 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Code Review
Summary
This is a clean, well-reasoned fix for the CS0121 ambiguity regression introduced in #5751. The diagnosis is accurate, the fix is surgical, and the changes are internally consistent.
Root Cause — Correct
[OverloadResolutionPriority] is a C# 13 language feature. Even when the attribute type is polyfilled (e.g., via the Polyfill package), older compilers do not honour it for overload resolution. The result is that on net8.0, both IsEqualTo<TValue>(TValue) and IsEqualTo<TValue, TOther>(TValue) are equally applicable for same-type call sites, triggering CS0121 and breaking every net8.0 consumer.
Fix — Correct and Minimal
Wrapping the entire ImplicitConversionEqualityExtensions.cs content in #if NET9_0_OR_GREATER is the right level of granularity:
- The net8.0 / netstandard2.0 assembly slices are now free of the two-generic overload — no ambiguity is possible.
- The net9.0 / net10.0 slices retain the feature and are consumed with at minimum a .NET 9 SDK (C# 13), so
[OverloadResolutionPriority(-1)]is honoured.
The header comment in the file clearly explains why the gate is there, which is exactly the kind of non-obvious reasoning worth documenting.
Consistency Checks
| Artefact | Expected | Actual |
|---|---|---|
ImplicitConversionEqualityExtensions.cs |
Gated to NET9_0_OR_GREATER |
✅ |
Issue5720Tests.cs |
Gated to match library | ✅ |
Issue5765Tests.cs (regression) |
No gate (single-generic overload only) | ✅ |
DotNet8_0.verified.txt / Net4_7.verified.txt |
Two-generic entries removed | ✅ |
DotNet9_0.verified.txt / DotNet10_0.verified.txt |
Unchanged | ✅ |
No .received.txt files committed |
Required by CLAUDE.md | ✅ |
Issues / Suggestions
1. Minor — Test coverage asymmetry
Issue5765Tests.cs has three IsEqualTo tests (enum, int, record) but only one IsNotEqualTo test (enum). For symmetry and full regression coverage, consider adding:
[Test]
public async Task IsNotEqualTo_SameType_Primitive_Compiles_And_Passes()
{
int value = 42;
await Assert.That(value).IsNotEqualTo(99);
}
[Test]
public async Task IsNotEqualTo_SameType_Record_Compiles_And_Passes()
{
var value = new SomeRecord("hello");
await Assert.That(value).IsNotEqualTo(new SomeRecord("world"));
}Not blocking, but it rounds out the regression suite.
2. Informational — Known limitation with old SDK on new TFM
The PR correctly notes in its description that a consumer targeting net9.0 with a .NET 8 SDK (C# 12) will still see CS0121. This is an acceptable edge case — the C# 13 / .NET 9 SDK pairing was the premise of #5751's design. Worth surfacing in release notes so users who hit it understand the upgrade path.
3. Informational — Alternative approach (not taken, and that's fine)
The referenced issue #5765 also suggested dropping the two-generic overload entirely and relying on C#'s existing implicit-conversion binding to the single-generic overload. That would have fixed the ambiguity universally across all TFMs without the net9+ restriction. The TFM-gate approach was chosen instead to preserve the explicit IsEqualTo<TValue, TOther> API for net9+ users. Both are valid; the gate is the better long-term choice here.
Verdict
The fix is correct. The snapshot updates, test gating, and regression tests are all consistent. The only real gap is the minor test coverage asymmetry for IsNotEqualTo on non-enum types. No architectural concerns, no AOT regressions, no dual-mode issues.
Per simplify review: - Shrunk the gate-rationale comment in ImplicitConversionEqualityExtensions from 9 lines to 4 — keep the WHY, drop the narration. - Replaced the duplicate gate-rationale block in Issue5720Tests with a one-line pointer to the source file. - Dropped the redundant XML <summary> on Issue5765Tests (sibling IssueNNNNTests files don't carry one) and the "_Compiles_And_Passes" suffix on each test name.
There was a problem hiding this comment.
Follow-up Review (after second commit)
The second commit () addressed the comment-trimming feedback from the previous review:
- Gate-rationale comment in trimmed from 9 lines to 4 — good.
- duplicate comment replaced with a concise one-liner — good.
- XML summary and verbose
_Compiles_And_Passestest name suffixes dropped — good.
Still Open: IsNotEqualTo test coverage asymmetry
The previous review flagged that Issue5765Tests.cs has three IsEqualTo cases (enum, int, record) but only one IsNotEqualTo case (enum). This wasn't addressed in the second commit. It's a minor point and not a blocker — the original CS0121 bug was symmetric across both methods (both overloads are gated together), so the existing test does prove the gate works for IsNotEqualTo. However, adding IsNotEqualTo for primitive and record types would make the regression suite fully symmetric:
[Test]
public async Task IsNotEqualTo_SameType_Primitive()
{
await Assert.That(42).IsNotEqualTo(99);
}
[Test]
public async Task IsNotEqualTo_SameType_Record()
{
var a = new Point(1, 2);
await Assert.That(a).IsNotEqualTo(new Point(3, 4));
}Not blocking — the fix is correct and complete. This is just a nice-to-have for coverage completeness.
Verdict
The fix is correct, the snapshots are consistent, the comments are now appropriately concise, and the test names are clean. Ready to merge once CI is green.
There was a problem hiding this comment.
Follow-up Review (after second commit)
The second commit (a7e239b) addressed the comment-trimming feedback from the previous review:
- Gate-rationale comment in
ImplicitConversionEqualityExtensions.cstrimmed from 9 lines to 4 — good. Issue5720Tests.csduplicate comment replaced with a concise one-liner — good.Issue5765Tests.csXML summary and verbose_Compiles_And_Passestest name suffixes dropped — good.
Still Open: IsNotEqualTo test coverage asymmetry
The previous review flagged that Issue5765Tests.cs has three IsEqualTo cases (enum, int, record) but only one IsNotEqualTo case (enum). This was not addressed in the second commit. It is a minor point and not a blocker — both overloads are gated together, so the existing enum test does prove the gate works for IsNotEqualTo. However, adding IsNotEqualTo tests for primitive and record types would make the regression suite fully symmetric:
[Test]
public async Task IsNotEqualTo_SameType_Primitive()
{
await Assert.That(42).IsNotEqualTo(99);
}
[Test]
public async Task IsNotEqualTo_SameType_Record()
{
var a = new Point(1, 2);
await Assert.That(a).IsNotEqualTo(new Point(3, 4));
}Not blocking — the fix is correct and complete. This is a nice-to-have for coverage completeness.
Verdict
The fix is correct, the snapshots are consistent, the comments are now appropriately concise, and the test names are clean. Ready to merge once CI is green.
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.39.0 to 1.40.5. <details> <summary>Release notes</summary> _Sourced from [TUnit's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.40.5 <!-- Release notes generated using configuration in .github/release.yml at v1.40.5 --> ## What's Changed ### Other Changes * Fix reflection property injection reuse by @thomhurst in thomhurst/TUnit#5763 * fix(assertions): gate IsEqualTo<TValue, TOther> overload to net9+ (#5765) by @thomhurst in thomhurst/TUnit#5767 ### Dependencies * chore(deps): update tunit to 1.40.0 by @thomhurst in thomhurst/TUnit#5762 **Full Changelog**: thomhurst/TUnit@v1.40.0...v1.40.5 ## 1.40.0 <!-- Release notes generated using configuration in .github/release.yml at v1.40.0 --> ## What's Changed ### Other Changes * perf(engine): collapse async forwarding wrappers in test execution (#5714) by @thomhurst in thomhurst/TUnit#5725 * perf(engine): skip Console.Out/Err FlushAsync when no output captured (#5712) by @thomhurst in thomhurst/TUnit#5724 * perf(engine): collapse async state machines on hook cache-hit / empty-hook path (#5713) by @thomhurst in thomhurst/TUnit#5726 * perf: eliminate per-test closure + GetOrAdd factory alloc (#5710) by @thomhurst in thomhurst/TUnit#5727 * perf(engine): replace global lock in EventReceiverRegistry with lock-free CAS by @thomhurst in thomhurst/TUnit#5731 * perf(engine): batch per-test overhead cleanups (#5719) by @thomhurst in thomhurst/TUnit#5730 * #5733 handling all arguments for Fact and Theory by @inyutin-maxim in thomhurst/TUnit#5734 * fix(assertions): prefer string overload of Member() over IEnumerable<char> (#5702) by @thomhurst in thomhurst/TUnit#5721 * fix(migration): preserve comments/XML docs when removing sole attributes (#5698) by @thomhurst in thomhurst/TUnit#5739 * perf(build): trim test TFMs and skip viewer dump by default by @thomhurst in thomhurst/TUnit#5741 * fix(pipeline): skip TestBaseModule frameworks with missing binaries by @thomhurst in thomhurst/TUnit#5752 * feat(assertions): focused diff messages for IsEqualTo/IsEquivalentTo (#5732) by @thomhurst in thomhurst/TUnit#5747 * fix(analyzers): remove incorrect AOT rules TUnit0300/0301/0302 (#5722) by @thomhurst in thomhurst/TUnit#5746 * perf(engine): lazy hook metadata registration (#5448) by @thomhurst in thomhurst/TUnit#5750 * chore(templates): unify TUnit version pinning to 1.* (#5709) by @thomhurst in thomhurst/TUnit#5743 * fix(templates): floating TUnit.Aspire version (#5708) by @thomhurst in thomhurst/TUnit#5742 * fix(assertions): preserve specialised source in .Count(itemAssertion) (#5707) by @thomhurst in thomhurst/TUnit#5749 * feat(assertions): IsEqualTo with implicitly-convertible wrappers (#5720) by @thomhurst in thomhurst/TUnit#5751 * feat(aspire): add ability to manually remove resources by @Odonno in thomhurst/TUnit#5586 * fix(fscheck): register default CancellationToken arbitrary that surfaces TestContext token by @JohnVerheij in thomhurst/TUnit#5758 * fix(engine): allow keyed NotInParallel tests to run alongside unconstrained tests (#5700) by @thomhurst in thomhurst/TUnit#5740 * perf: skip TimeoutHelper wrap when no explicit [Timeout] is set (#5711) by @thomhurst in thomhurst/TUnit#5728 ### Dependencies * chore(deps): update tunit to 1.39.0 by @thomhurst in thomhurst/TUnit#5701 * chore(deps): update aspire to 13.2.4 by @thomhurst in thomhurst/TUnit#5735 * chore(deps): bump postcss from 8.5.6 to 8.5.10 in /docs by @dependabot[bot] in thomhurst/TUnit#5736 * chore(deps): update dependency fscheck to 3.3.3 by @thomhurst in thomhurst/TUnit#5760 ## New Contributors * @inyutin-maxim made their first contribution in thomhurst/TUnit#5734 * @Odonno made their first contribution in thomhurst/TUnit#5586 **Full Changelog**: thomhurst/TUnit@v1.39.0...v1.40.0 Commits viewable in [compare view](thomhurst/TUnit@v1.39.0...v1.40.5). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.37.0 to 1.40.10. <details> <summary>Release notes</summary> _Sourced from [TUnit.Core's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.40.10 <!-- Release notes generated using configuration in .github/release.yml at v1.40.10 --> ## What's Changed ### Other Changes * refactor(opentelemetry): depend on TUnit.Core instead of umbrella TUnit by @thomhurst in thomhurst/TUnit#5774 ### Dependencies * chore(deps): update tunit to 1.40.5 by @thomhurst in thomhurst/TUnit#5769 **Full Changelog**: thomhurst/TUnit@v1.40.5...v1.40.10 ## 1.40.5 <!-- Release notes generated using configuration in .github/release.yml at v1.40.5 --> ## What's Changed ### Other Changes * Fix reflection property injection reuse by @thomhurst in thomhurst/TUnit#5763 * fix(assertions): gate IsEqualTo<TValue, TOther> overload to net9+ (#5765) by @thomhurst in thomhurst/TUnit#5767 ### Dependencies * chore(deps): update tunit to 1.40.0 by @thomhurst in thomhurst/TUnit#5762 **Full Changelog**: thomhurst/TUnit@v1.40.0...v1.40.5 ## 1.40.0 <!-- Release notes generated using configuration in .github/release.yml at v1.40.0 --> ## What's Changed ### Other Changes * perf(engine): collapse async forwarding wrappers in test execution (#5714) by @thomhurst in thomhurst/TUnit#5725 * perf(engine): skip Console.Out/Err FlushAsync when no output captured (#5712) by @thomhurst in thomhurst/TUnit#5724 * perf(engine): collapse async state machines on hook cache-hit / empty-hook path (#5713) by @thomhurst in thomhurst/TUnit#5726 * perf: eliminate per-test closure + GetOrAdd factory alloc (#5710) by @thomhurst in thomhurst/TUnit#5727 * perf(engine): replace global lock in EventReceiverRegistry with lock-free CAS by @thomhurst in thomhurst/TUnit#5731 * perf(engine): batch per-test overhead cleanups (#5719) by @thomhurst in thomhurst/TUnit#5730 * #5733 handling all arguments for Fact and Theory by @inyutin-maxim in thomhurst/TUnit#5734 * fix(assertions): prefer string overload of Member() over IEnumerable<char> (#5702) by @thomhurst in thomhurst/TUnit#5721 * fix(migration): preserve comments/XML docs when removing sole attributes (#5698) by @thomhurst in thomhurst/TUnit#5739 * perf(build): trim test TFMs and skip viewer dump by default by @thomhurst in thomhurst/TUnit#5741 * fix(pipeline): skip TestBaseModule frameworks with missing binaries by @thomhurst in thomhurst/TUnit#5752 * feat(assertions): focused diff messages for IsEqualTo/IsEquivalentTo (#5732) by @thomhurst in thomhurst/TUnit#5747 * fix(analyzers): remove incorrect AOT rules TUnit0300/0301/0302 (#5722) by @thomhurst in thomhurst/TUnit#5746 * perf(engine): lazy hook metadata registration (#5448) by @thomhurst in thomhurst/TUnit#5750 * chore(templates): unify TUnit version pinning to 1.* (#5709) by @thomhurst in thomhurst/TUnit#5743 * fix(templates): floating TUnit.Aspire version (#5708) by @thomhurst in thomhurst/TUnit#5742 * fix(assertions): preserve specialised source in .Count(itemAssertion) (#5707) by @thomhurst in thomhurst/TUnit#5749 * feat(assertions): IsEqualTo with implicitly-convertible wrappers (#5720) by @thomhurst in thomhurst/TUnit#5751 * feat(aspire): add ability to manually remove resources by @Odonno in thomhurst/TUnit#5586 * fix(fscheck): register default CancellationToken arbitrary that surfaces TestContext token by @JohnVerheij in thomhurst/TUnit#5758 * fix(engine): allow keyed NotInParallel tests to run alongside unconstrained tests (#5700) by @thomhurst in thomhurst/TUnit#5740 * perf: skip TimeoutHelper wrap when no explicit [Timeout] is set (#5711) by @thomhurst in thomhurst/TUnit#5728 ### Dependencies * chore(deps): update tunit to 1.39.0 by @thomhurst in thomhurst/TUnit#5701 * chore(deps): update aspire to 13.2.4 by @thomhurst in thomhurst/TUnit#5735 * chore(deps): bump postcss from 8.5.6 to 8.5.10 in /docs by @dependabot[bot] in thomhurst/TUnit#5736 * chore(deps): update dependency fscheck to 3.3.3 by @thomhurst in thomhurst/TUnit#5760 ## New Contributors * @inyutin-maxim made their first contribution in thomhurst/TUnit#5734 * @Odonno made their first contribution in thomhurst/TUnit#5586 **Full Changelog**: thomhurst/TUnit@v1.39.0...v1.40.0 ## 1.39.0 <!-- Release notes generated using configuration in .github/release.yml at v1.39.0 --> ## What's Changed ### Other Changes * perf(mocks): shrink MethodSetup + cache stateless matchers by @thomhurst in thomhurst/TUnit#5669 * fix(mocks): handle base classes with explicit interface impls (#5673) by @thomhurst in thomhurst/TUnit#5674 * fix(mocks): implement indexer in generated mock (#5676) by @thomhurst in thomhurst/TUnit#5683 * fix(mocks): disambiguate IEquatable<T>.Equals from object.Equals (#5675) by @thomhurst in thomhurst/TUnit#5680 * fix(mocks): escape C# keyword identifiers at all emit sites (#5679) by @thomhurst in thomhurst/TUnit#5684 * fix(mocks): emit [SetsRequiredMembers] on generated mock ctor (#5678) by @thomhurst in thomhurst/TUnit#5682 * fix(mocks): skip MockBridge for class targets with static-abstract interfaces (#5677) by @thomhurst in thomhurst/TUnit#5681 * chore(mocks): regenerate source generator snapshots by @thomhurst in thomhurst/TUnit#5691 * perf(engine): collapse async state-machine layers on hot test path (#5687) by @thomhurst in thomhurst/TUnit#5690 * perf(engine): reduce lock contention in scheduling and hook caches (#5686) by @thomhurst in thomhurst/TUnit#5693 * fix(assertions): prevent implicit-to-string op from NREing on null (#5692) by @thomhurst in thomhurst/TUnit#5696 * perf(engine/core): reduce per-test allocations (#5688) by @thomhurst in thomhurst/TUnit#5694 * perf(engine): reduce message-bus contention on test start (#5685) by @thomhurst in thomhurst/TUnit#5695 ### Dependencies * chore(deps): update tunit to 1.37.36 by @thomhurst in thomhurst/TUnit#5667 * chore(deps): update verify to 31.16.2 by @thomhurst in thomhurst/TUnit#5699 **Full Changelog**: thomhurst/TUnit@v1.37.36...v1.39.0 ## 1.37.36 <!-- Release notes generated using configuration in .github/release.yml at v1.37.36 --> ## What's Changed ### Other Changes * fix(telemetry): remove duplicate HTTP client spans by @thomhurst in thomhurst/TUnit#5668 **Full Changelog**: thomhurst/TUnit@v1.37.35...v1.37.36 ## 1.37.35 <!-- Release notes generated using configuration in .github/release.yml at v1.37.35 --> ## What's Changed ### Other Changes * Add TUnit.TestProject.Library to the TUnit.Dev.slnx solution file by @Zodt in thomhurst/TUnit#5655 * fix(aspire): preserve user-supplied OTLP endpoint (#4818) by @thomhurst in thomhurst/TUnit#5665 * feat(aspire): emit client spans for HTTP by @thomhurst in thomhurst/TUnit#5666 ### Dependencies * chore(deps): update dependency dotnet-sdk to v10.0.203 by @thomhurst in thomhurst/TUnit#5656 * chore(deps): update microsoft.aspnetcore to 10.0.7 by @thomhurst in thomhurst/TUnit#5657 * chore(deps): update tunit to 1.37.24 by @thomhurst in thomhurst/TUnit#5659 * chore(deps): update microsoft.extensions to 10.0.7 by @thomhurst in thomhurst/TUnit#5658 * chore(deps): update aspire to 13.2.3 by @thomhurst in thomhurst/TUnit#5661 * chore(deps): update dependency microsoft.net.test.sdk to 18.5.0 by @thomhurst in thomhurst/TUnit#5664 ## New Contributors * @Zodt made their first contribution in thomhurst/TUnit#5655 **Full Changelog**: thomhurst/TUnit@v1.37.24...v1.37.35 ## 1.37.24 <!-- Release notes generated using configuration in .github/release.yml at v1.37.24 --> ## What's Changed ### Other Changes * docs: add Tluma Ask AI widget to Docusaurus site by @thomhurst in thomhurst/TUnit#5638 * Revert "chore(deps): update dependency docusaurus-plugin-llms to ^0.4.0 (#5637)" by @thomhurst in thomhurst/TUnit#5640 * fix(asp-net): forward disposal in FlowSuppressingHostedService (#5651) by @JohnVerheij in thomhurst/TUnit#5652 ### Dependencies * chore(deps): update dependency docusaurus-plugin-llms to ^0.4.0 by @thomhurst in thomhurst/TUnit#5637 * chore(deps): update tunit to 1.37.10 by @thomhurst in thomhurst/TUnit#5639 * chore(deps): update opentelemetry to 1.15.3 by @thomhurst in thomhurst/TUnit#5645 * chore(deps): update opentelemetry by @thomhurst in thomhurst/TUnit#5647 * chore(deps): update dependency dompurify to v3.4.1 by @thomhurst in thomhurst/TUnit#5648 * chore(deps): update dependency system.commandline to 2.0.7 by @thomhurst in thomhurst/TUnit#5650 * chore(deps): update dependency microsoft.entityframeworkcore to 10.0.7 by @thomhurst in thomhurst/TUnit#5649 * chore(deps): update dependency microsoft.templateengine.authoring.cli to v10.0.203 by @thomhurst in thomhurst/TUnit#5653 * chore(deps): update dependency microsoft.templateengine.authoring.templateverifier to 10.0.203 by @thomhurst in thomhurst/TUnit#5654 **Full Changelog**: thomhurst/TUnit@v1.37.10...v1.37.24 ## 1.37.10 <!-- Release notes generated using configuration in .github/release.yml at v1.37.10 --> ## What's Changed ### Other Changes * docs(test-filters): add migration callout for --filter → --treenode-filter by @johnkattenhorn in thomhurst/TUnit#5628 * fix: re-enable RPC tests and modernize harness (#5540) by @thomhurst in thomhurst/TUnit#5632 * fix(mocks): propagate [Obsolete] and null-forgiving raise dispatch (#5626) by @JohnVerheij in thomhurst/TUnit#5631 * ci: use setup-dotnet built-in NuGet cache by @thomhurst in thomhurst/TUnit#5635 * feat(playwright): propagate W3C trace context into browser contexts by @thomhurst in thomhurst/TUnit#5636 ### Dependencies * chore(deps): update tunit to 1.37.0 by @thomhurst in thomhurst/TUnit#5625 ## New Contributors * @johnkattenhorn made their first contribution in thomhurst/TUnit#5628 * @JohnVerheij made their first contribution in thomhurst/TUnit#5631 **Full Changelog**: thomhurst/TUnit@v1.37.0...v1.37.10 Commits viewable in [compare view](thomhurst/TUnit@v1.37.0...v1.40.10). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Fixes #5765.
Summary
IsEqualTo<TValue, TOther>/IsNotEqualTo<TValue, TOther>overloads added in 1.40.0 (feat(assertions): IsEqualTo with implicitly-convertible wrappers (#5720) #5751) rely on[OverloadResolutionPriority(-1)]to lose to the source-generated single-generic overload when both apply. That attribute is only honored by C# 13+ and only present inSystem.Runtime.CompilerServiceson .NET 9+. On thenet8.0/netstandard2.0builds ofTUnit.Assertions, Polyfill silently drops the attribute and every same-typeIsEqualTocall becomes ambiguous (CS0121) — breaking effectively every existing test suite.ImplicitConversionCachethey use) behind#if NET9_0_OR_GREATERinImplicitConversionEqualityExtensions.cs. Onnet8.0/netstandard2.0the new overload simply doesn't exist, so the original well-defined overload binds without contest.net8.0andnetstandard2.0(Net4_7) updated to drop the gated overloads.Issue5720Tests(which exercises the gated overload) gated to match.Issue5765Testsregression covering same-typeIsEqualTofor enum / primitive / record across all TFMs.Verification
Reproduced CS0121 against
TUnit1.40.0 nuget:With this fix applied, the same repro
dotnet builds clean onnet8.0,net9.0, andnet10.0.Inspected the built assemblies to confirm the gate landed where expected:
ImplicitConversionCacheIsEqualTosymbolsnet8.0net9.0net10.0Test suite results after the change:
net8.0: 1999 / 1999 passed (Issue5720Tests gated out)net9.0: 2066 / 2066 passednet10.0: 2066 / 2066 passedKnown limitation worth flagging in release notes
A consumer using .NET 8 SDK (C# 12) that targets
net9.0/net10.0will still hit CS0121, because their compiler does not honor[OverloadResolutionPriority]. This matches what the issue reporter likely encountered when they noted reproduction on net 9 / 10 too. The fix on their side is to upgrade to .NET 9+ SDK.Test plan
net8.0against TUnit 1.40.0 nuget — confirmednet8.0,net9.0,net10.0dotnet buildTUnit.Assertionsfor all TFMs — cleandotnet buildTUnit.Assertions.Testsfor all TFMs — cleanIssue5765Tests+Issue5720Tests+NullabilityWarningTestsonnet8.0/net9.0/net10.0— all green