Fix MRW context base model discovery#11055
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
commit: |
|
No changes needing a change description found. |
|
No diff in regen: Azure/azure-sdk-for-net#60155 |
ReviewVerdict: correct fix for the reported bug, with one recursion-safety concern worth raising.Why the fix works ✅
Base models are still never emitted as standalone context entries ( 🟡 Concern: base traversal loses its only cycle guardThe changed line now recurses into the base chain via Suggestion: add a reference-equality recursion guard for the base traversal, e.g.: if (provider is ModelProvider modelProvider && modelProvider.BaseModelProvider != null
&& visitedBaseProviders.Add(modelProvider.BaseModelProvider)) // ReferenceEqualityComparer set
{
CollectBuildableTypesRecursiveCore(modelProvider.BaseModelProvider, ...);
}This fixes the name-collision bug and preserves protection against cyclic base chains. A reference-equality guard won''t reintroduce the original bug because the two same-named base instances are distinct references. 🟢 Minor (non-blocking)Shared base subtrees are now re-traversed once per derived model (the base no longer gets memoized in --generated by Copilot |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed in Validation:
|
…ext-base-discovery
JoshLove-msft
left a comment
There was a problem hiding this comment.
Review — LGTM ✅
I reviewed the fix and verified it locally (built + ran the two new tests, and re-ran them against the pre-fix source).
Correctness
The change replaces base-model recursion with a CollectBuildableTypesRecursiveCore call guarded by a new visitedBaseProviders set using ReferenceEqualityComparer. This fixes two real issues at once:
- Dropped types: name-based dedup (
s_typeProviderNameComparer) previously suppressed traversal of a base provider whose type name was already visited from an output-library root, so framework MRW types reachable only through that base provider's properties (e.g.ModelReaderWriterBuildableforFrameworkModelWithMRW) were lost. Reference equality no longer over-collapses distinct same-named base instances. - Cycle safety: the pre-fix base traversal had no cycle guard — I confirmed
ValidateCyclicBaseProviderTraversalStopstriggers aStackOverflowException(test run aborted) against the old source, and terminates correctly with the fix.
Verification
ValidateDuplicateBaseProviderNameStillDiscoversFrameworkTypesandValidateCyclicBaseProviderTraversalStopsboth pass with the fix and fail/abort without it — genuine regression guards.- Confirmed
new HashSet<TypeProvider>(ReferenceEqualityComparer.Instance)compiles (net8+).
Minor (non-blocking)
visitedBaseProvidersis created once perCollectBuildableTypes()and reference-keyed, so a base provider instance shared across multiple derived models is traversed only once globally. That's correct since buildable types are accumulated globally, but worth noting it relies on shared base providers being the same instance.
Nice, surgical fix with good coverage.
--generated by Copilot
Description
Fixes
ModelReaderWriterContextDefinitionso recursive base-model traversal still discovers framework model reader/writer types when an equivalent provider name was already visited from the output library roots.This prevents context attributes such as
ModelReaderWriterBuildablefrom being dropped for framework MRW types referenced through base provider properties.Validation
dotnet test Microsoft.TypeSpec.Generator.ClientModel/test/Microsoft.TypeSpec.Generator.ClientModel.Tests.csproj --filter FullyQualifiedName~ModelReaderWriterContextDefinitionTests.ValidateDuplicateBaseProviderNameStillDiscoversFrameworkTypes --no-restoredotnet test Microsoft.TypeSpec.Generator.ClientModel/test/Microsoft.TypeSpec.Generator.ClientModel.Tests.csproj --no-restoregit diff --check