fix(mocks): emit 'where T : default' for interface-constrained generic methods#6458
Conversation
…c methods
Explicit interface implementations inherit constraints, but 'T?' in them is
parsed as Nullable<T> unless the type parameter is known to be a reference or
value type, or a 'where T : default' clause is present. The generator only
emitted 'default' for fully unconstrained type parameters, so interfaces like
Kiota's IRequestAdapter ('where ModelType : IParsable') generated mocks that
failed with CS0539/CS0535/CS0453/CS0314.
Gate the clause on ITypeParameterSymbol.IsReferenceType/IsValueType instead,
which Roslyn computes transitively across base-class and type-parameter
constraints.
Fixes #6456
Greptile SummaryThis PR fixes mock generation for nullable generic methods constrained only by interfaces. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (1): Last reviewed commit: "fix(mocks): emit 'where T : default' for..." | Re-trigger Greptile |
There was a problem hiding this comment.
Code review
Summary: Fixes #6456 by broadening the gate for emitting where T : default on explicit interface implementations from "fully unconstrained" (IsUnconstrained()) to "not known to be a reference or value type" (IsReferenceType/IsValueType). This correctly covers interface-only constraints like Kiota's where ModelType : IParsable. The root-cause analysis is sound, and IsReferenceType/IsValueType are computed transitively by Roslyn, so the fix also correctly handles base-class/type-parameter constraint chains, not just literal class/struct keywords. I checked the two existing snapshots that already exercise the default clause path (Interface_FluentUI_Shape_Nullable_Warnings.verified.txt, Interface_With_Unconstrained_Nullable_Generic.verified.txt) — both are fully-unconstrained cases, so they're unaffected by the widened gate. No regressions expected there.
Suggestion — test dependency choice: KiotaMockTests.cs (and the runtime counterpart Issue6456Tests.cs) pull in a real third-party package (Microsoft.Kiota.Abstractions, new PackageVersion + two new PackageReferences) purely to reproduce the interface-constraint shape. That's a departure from this test suite's existing convention: Interface_FluentUI_Shape_Nullable_Warnings in tests/TUnit.Mocks.SourceGenerator.Tests/MockGeneratorTests.cs:1961 deliberately uses a synthetic in-source interface shaped like FluentUI's IDialogService rather than referencing the real FluentUI package — with a comment noting earlier synthetic repros needed iteration to match the real-world shape exactly.
The same approach would work here: a local interface with where ModelType : ISomeMarker reproduces "interface-only constraint on an explicit-impl generic method" without a live NuGet dependency. The bug is fundamentally about Roslyn constraint-resolution semantics, not anything Kiota-specific, so a synthetic repro would be just as effective at pinning the regression while avoiding restore/network dependency and version-drift risk in the source-generator test project.
I'd keep the one small end-to-end runtime test against real Kiota (Issue6456Tests.cs) for exact bug-report fidelity, but convert the source-generator-level compile check (KiotaMockTests.cs) to a synthetic repro consistent with the FluentUI precedent — that drops one of the two new package dependencies while keeping equivalent coverage.
Everything else — the rename (IsUnconstrainedWithNullableUsage → NeedsDefaultConstraintForNullableUsage), the explanatory comment, and the tests' actual assertions — looks solid and low-risk. No CLAUDE.md compliance issues found (this touches TUnit.Mocks.SourceGenerator, not core engine metadata collection, so the dual-mode rule doesn't apply).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f933b06ab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Summary
Fixes #6456 — mocking
Microsoft.Kiota.Abstractions.IRequestAdapterfailed to compile with CS0539/CS0535/CS0453/CS0314.Root cause
Kiota's generic methods carry an interface constraint:
In an explicit interface implementation, constraints are inherited and
T?is parsed asNullable<T>unlessTis known to be a reference/value type or awhere T : defaultclause is present. The generator only emitteddefaultfor fully unconstrained type parameters (IsUnconstrained()gate), so an interface-only constraint likeIParsable— which leavesTneither known-reference nor known-value — got no clause. The compiler then readTask<ModelType?>asTask<Nullable<ModelType>>, the signature no longer matched the interface member, and everything cascaded.Fix
Gate the
defaultclause onITypeParameterSymbol.IsReferenceType/IsValueTypeinstead, which Roslyn computes transitively (base-class constraints, type-parameter constraints). Type parameters constrained only by interfaces/notnull/new()now correctly getwhere T : defaultwhenT?appears in the signature; known-reference (class, base-class) and known-value (struct,unmanaged) parameters still don't.Tests
KiotaMockTests(source-gen): generates a mock for the realMicrosoft.Kiota.Abstractions.IRequestAdapter(new package reference), asserts thewhere ModelType : defaultclause is emitted and none of the four reported error codes are produced.Issue6456Tests(runtime): mocksIRequestAdapter, verifiesSendNoContentAsyncand a configuredSendAsync<TestModel>with anIParsable-constrained model — mirrors the issue repro.TUnit.Mocks.SourceGenerator.Tests(78) andTUnit.Mocks.Tests(1160) pass on net10.0; all TFMs (net472/net8/net9/net10) build.