Skip to content

Commit fa1ae2f

Browse files
committed
refsctored and fixed ci failures
1 parent 8761857 commit fa1ae2f

File tree

1 file changed

+28
-112
lines changed
  • packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers

1 file changed

+28
-112
lines changed

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs

Lines changed: 28 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ protected override FormattableString BuildDescription()
5454
}
5555

5656
private readonly bool _isAbstract;
57+
private readonly bool _isMultiLevelDiscriminator;
5758

5859
private readonly CSharpType _additionalBinaryDataPropsFieldType = typeof(IDictionary<string, BinaryData>);
5960
private readonly Type _additionalPropsUnknownType = typeof(BinaryData);
@@ -68,6 +69,7 @@ public ModelProvider(InputModelType inputModel) : base(inputModel)
6869
{
6970
_inputModel = inputModel;
7071
_isAbstract = _inputModel.DiscriminatorProperty is not null && _inputModel.DiscriminatorValue is null;
72+
_isMultiLevelDiscriminator = ComputeIsMultiLevelDiscriminator();
7173

7274
if (_inputModel.BaseModel is not null)
7375
{
@@ -524,12 +526,6 @@ protected override ConstructorProvider[] BuildConstructors()
524526
return [FullConstructor];
525527
}
526528

527-
// Check if this model needs the dual constructor pattern
528-
if (ShouldHaveDualConstructorPattern())
529-
{
530-
return BuildDualConstructorPattern();
531-
}
532-
533529
// Build the standard single initialization constructor
534530
var accessibility = DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)
535531
? MethodSignatureModifiers.Private | MethodSignatureModifiers.Protected
@@ -551,23 +547,33 @@ protected override ConstructorProvider[] BuildConstructors()
551547
},
552548
this);
553549

550+
var constructors = new List<ConstructorProvider> { constructor };
551+
552+
// Add FullConstructor if parameters are different
554553
if (!constructorParameters.SequenceEqual(FullConstructor.Signature.Parameters))
555554
{
556-
return [constructor, FullConstructor];
555+
constructors.Add(FullConstructor);
556+
}
557+
558+
// For multi-level discriminators, add one additional private protected constructor
559+
if (_isMultiLevelDiscriminator)
560+
{
561+
var protectedConstructor = BuildProtectedInheritanceConstructor();
562+
constructors.Add(protectedConstructor);
557563
}
558564

559-
return [constructor];
565+
return [.. constructors];
560566
}
561567

562568
/// <summary>
563569
/// Determines if this model should have a dual constructor pattern.
564570
/// This is needed when the model shares the same discriminator property name as its base model
565571
/// AND has derived models, indicating it's an intermediate type in a discriminated union hierarchy.
566572
/// </summary>
567-
private bool ShouldHaveDualConstructorPattern()
573+
private bool ComputeIsMultiLevelDiscriminator()
568574
{
569575
// Only applies to non-abstract models with a base model
570-
if (_isAbstract || BaseModelProvider == null)
576+
if (_isAbstract || _inputModel.BaseModel == null)
571577
{
572578
return false;
573579
}
@@ -584,7 +590,7 @@ private bool ShouldHaveDualConstructorPattern()
584590
}
585591

586592
// Check if base model has a discriminator property with the same name
587-
if (BaseModelProvider._inputModel.DiscriminatorProperty == null)
593+
if (_inputModel.BaseModel.DiscriminatorProperty == null)
588594
{
589595
return false;
590596
}
@@ -593,119 +599,30 @@ private bool ShouldHaveDualConstructorPattern()
593599
// and this model has derived models, it needs the dual constructor pattern
594600
return string.Equals(
595601
_inputModel.DiscriminatorProperty.Name,
596-
BaseModelProvider._inputModel.DiscriminatorProperty.Name,
602+
_inputModel.BaseModel.DiscriminatorProperty.Name,
597603
StringComparison.OrdinalIgnoreCase);
598604
}
599605

600606
/// <summary>
601-
/// Builds the dual constructor pattern for models that share discriminator properties with their base.
602-
/// Creates three constructors:
603-
/// 1. Public constructor for external use
604-
/// 2. Private protected constructor for derived models to call
605-
/// 3. Internal constructor for serialization
607+
/// Builds a private protected constructor for multi-level discriminator inheritance.
608+
/// This allows derived models to call this constructor with their discriminator value.
606609
/// </summary>
607-
private ConstructorProvider[] BuildDualConstructorPattern()
610+
private ConstructorProvider BuildProtectedInheritanceConstructor()
608611
{
609-
// Build public constructor (without discriminator parameter)
610-
var (publicParams, publicInitializer) = BuildPublicInstantiationParameters();
611-
var publicConstructor = new ConstructorProvider(
612-
signature: new ConstructorSignature(
613-
Type,
614-
$"Initializes a new instance of {Type:C}",
615-
MethodSignatureModifiers.Public,
616-
publicParams,
617-
initializer: publicInitializer),
618-
bodyStatements: new MethodBodyStatement[]
619-
{
620-
GetPropertyInitializers(true, parameters: publicParams)
621-
},
622-
this);
612+
var (parameters, initializer) = BuildConstructorParameters(true, includeDiscriminatorParameter: true);
623613

624-
// Build private protected constructor (with discriminator parameter)
625-
var (protectedParams, protectedInitializer) = BuildPrivateProtectedInheritanceParameters();
626-
var protectedConstructor = new ConstructorProvider(
614+
return new ConstructorProvider(
627615
signature: new ConstructorSignature(
628616
Type,
629617
$"Initializes a new instance of {Type:C}",
630618
MethodSignatureModifiers.Private | MethodSignatureModifiers.Protected,
631-
protectedParams,
632-
initializer: protectedInitializer),
619+
parameters,
620+
initializer: initializer),
633621
bodyStatements: new MethodBodyStatement[]
634622
{
635-
GetPropertyInitializers(true, parameters: protectedParams)
623+
GetPropertyInitializers(true, parameters: parameters)
636624
},
637625
this);
638-
639-
// Internal constructor is the full constructor
640-
return [publicConstructor, protectedConstructor, FullConstructor];
641-
}
642-
643-
/// <summary>
644-
/// Builds parameters for public instantiation constructor (filters out discriminator).
645-
/// </summary>
646-
private (IReadOnlyList<ParameterProvider> Parameters, ConstructorInitializer? Initializer) BuildPublicInstantiationParameters()
647-
{
648-
var (standardParams, _) = BuildConstructorParameters(true);
649-
650-
var parameters = standardParams.Where(p => p.Property == null || !p.Property.IsDiscriminator).ToList();
651-
652-
var initializer = CreatePrivateProtectedConstructorCall(parameters);
653-
654-
return (parameters, initializer);
655-
}
656-
657-
/// <summary>
658-
/// Creates a constructor initializer that calls the private protected constructor with discriminator value.
659-
/// </summary>
660-
private ConstructorInitializer CreatePrivateProtectedConstructorCall(IReadOnlyList<ParameterProvider> parameters)
661-
{
662-
var discriminatorValue = EnsureDiscriminatorValueExpression();
663-
var args = new List<ValueExpression>();
664-
665-
if (discriminatorValue != null)
666-
{
667-
args.Add(discriminatorValue);
668-
}
669-
670-
var overriddenProperties = CanonicalView.Properties.Where(p => p.BaseProperty is not null).Select(p => p.BaseProperty!).ToHashSet();
671-
args.AddRange(parameters.Select(p => GetExpressionForCtor(p, overriddenProperties, true)));
672-
673-
return new ConstructorInitializer(false, args.ToArray());
674-
}
675-
676-
/// <summary>
677-
/// Builds parameters for private protected inheritance constructor (includes discriminator).
678-
/// </summary>
679-
private (IReadOnlyList<ParameterProvider> Parameters, ConstructorInitializer? Initializer) BuildPrivateProtectedInheritanceParameters()
680-
{
681-
var (parameters, standardInitializer) = BuildConstructorParameters(true, includeDiscriminatorParameter: true);
682-
683-
var initializer = CreateBaseConstructorCallWithDiscriminatorParameter(standardInitializer, parameters.FirstOrDefault());
684-
685-
return (parameters, initializer);
686-
}
687-
688-
/// <summary>
689-
/// Creates a base constructor call that uses the discriminator parameter.
690-
/// </summary>
691-
private ConstructorInitializer? CreateBaseConstructorCallWithDiscriminatorParameter(
692-
ConstructorInitializer? standardInitializer,
693-
ParameterProvider? discriminatorParam)
694-
{
695-
if (BaseModelProvider?.Type is null || standardInitializer is null || discriminatorParam is null)
696-
return standardInitializer;
697-
698-
var originalArgs = standardInitializer.Arguments;
699-
if (originalArgs.Count == 0)
700-
return standardInitializer;
701-
702-
var newArgs = new List<ValueExpression>
703-
{
704-
discriminatorParam.AsVariable()
705-
};
706-
newArgs.AddRange(originalArgs.Skip(1));
707-
708-
return new ConstructorInitializer(standardInitializer.IsBase, newArgs);
709626
}
710627

711628
/// <summary>
@@ -818,7 +735,7 @@ private IEnumerable<FieldProvider> GetAllBaseFieldsForConstructorInitialization(
818735
if (baseParameters.Count > 0)
819736
{
820737
// Check if base model has dual constructor pattern and we should call private protected constructor
821-
if (isPrimaryConstructor && BaseModelProvider.ShouldHaveDualConstructorPattern())
738+
if (isPrimaryConstructor && BaseModelProvider._isMultiLevelDiscriminator)
822739
{
823740
// Call base model's private protected constructor with discriminator value
824741
var args = new List<ValueExpression>();
@@ -853,8 +770,7 @@ private IEnumerable<FieldProvider> GetAllBaseFieldsForConstructorInitialization(
853770
? baseParameters
854771
: baseParameters.Where(p =>
855772
p.Property is null
856-
|| (p.Property.IsDiscriminator && !overriddenProperties.Contains(p.Property) && (!isPrimaryConstructor || includeDiscriminatorParameter))
857-
|| (!p.Property.IsDiscriminator && !overriddenProperties.Contains(p.Property))));
773+
|| (!overriddenProperties.Contains(p.Property!) && (!p.Property.IsDiscriminator || !isPrimaryConstructor || includeDiscriminatorParameter))));
858774

859775
if (includeDiscriminatorParameter && _inputModel.DiscriminatorProperty != null)
860776
{

0 commit comments

Comments
 (0)