Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ private bool TryBuildCompatibleMethodForPreviousContract(
continue;
}

var model = GetModelToInstantiateForFactoryMethod(modelProvider);
if (model != null && previousMethodReturnType.AreNamesEqual(model.Type))
var model = GetModelToInstantiateForPreviousReturnType(modelProvider, previousMethodReturnType);
if (model != null)
{
modelToInstantiate = model;
break;
Expand Down Expand Up @@ -525,6 +525,26 @@ private static IReadOnlyList<ValueExpression> GetCtorArgs(
: modelProvider;
}

private static ModelProvider? GetModelToInstantiateForPreviousReturnType(ModelProvider modelProvider, CSharpType returnType)
{
var model = GetModelToInstantiateForFactoryMethod(modelProvider);
if (model is null)
{
return null;
}

if (modelProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract))
{
return returnType.AreNamesEqual(modelProvider.Type) || returnType.AreNamesEqual(model.Type)
? model
: null;
}

return returnType.AreNamesEqual(model.Type)
? model
: null;
}

private static (ParameterProvider? BinaryDataParam, ConstructorProvider FullCtor) GetBinaryDataParamAndFullCtorForFactoryMethod(
ModelProvider modelProvider)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,57 @@ public async Task BackCompatibility_SwapTypeParamsDoesNotCorrupt()
result);
}

[Test]
public async Task BackCompatibility_AbstractReturnTypeOverloadIsGenerated()
{
var derived = InputFactory.Model("DerivedModel", discriminatedKind: "derived");
var baseModel = InputFactory.Model(
"AbstractModel",
properties:
[
InputFactory.Property("kind", InputPrimitiveType.String, isRequired: true, isDiscriminator: true),
InputFactory.Property("prop1", InputPrimitiveType.String),
InputFactory.Property("prop2", InputPrimitiveType.String),
],
derivedModels: [derived]);

_instance = (await MockHelpers.LoadMockGeneratorAsync(
inputNamespaceName: "Sample.Namespace",
inputModelTypes: [baseModel, derived],
lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync())).Object;

var modelFactory = _instance!.OutputLibrary.ModelFactory.Value;
modelFactory.ProcessTypeForBackCompatibility();

var content = new TypeProviderWriter(modelFactory).Write().Content;
Assert.AreEqual(Helpers.GetExpectedFromFile(), content);
}

[Test]
public async Task BackCompatibility_UnknownDiscriminatorReturnTypeOverloadIsGenerated()
{
var derived = InputFactory.Model("DerivedModel", discriminatedKind: "derived");
var baseModel = InputFactory.Model(
"AbstractModel",
properties:
[
InputFactory.Property("kind", InputPrimitiveType.String, isRequired: true, isDiscriminator: true),
InputFactory.Property("prop1", InputPrimitiveType.String),
],
derivedModels: [derived]);

_instance = (await MockHelpers.LoadMockGeneratorAsync(
inputNamespaceName: "Sample.Namespace",
inputModelTypes: [baseModel, derived],
lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync())).Object;

var modelFactory = _instance!.OutputLibrary.ModelFactory.Value;
modelFactory.ProcessTypeForBackCompatibility();

var content = new TypeProviderWriter(modelFactory).Write().Content;
Assert.AreEqual(Helpers.GetExpectedFromFile(), content);
}

[Test]
public void RequiredConstantPropertiesAreNotExposedAsParameters()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// <auto-generated/>

#nullable disable

using System.ComponentModel;
using Sample.Models;

namespace Sample.Namespace
{
public static partial class SampleNamespaceModelFactory
{
public static global::Sample.Models.AbstractModel AbstractModel(string kind = default, string prop1 = default, string prop2 = default)
{
return new global::Sample.Models.UnknownAbstractModel(kind, prop1, prop2, additionalBinaryDataProperties: null);
}

public static global::Sample.Models.DerivedModel DerivedModel(string prop1 = default, string prop2 = default, string stringProperty = default)
{
return new global::Sample.Models.DerivedModel("derived", prop1, prop2, additionalBinaryDataProperties: null, stringProperty);
}

[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::Sample.Models.AbstractModel AbstractModel(string prop1, string kind)
{
return AbstractModel(kind: kind, prop1: prop1, prop2: default);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Sample.Models;

namespace Sample.Namespace
{
public static partial class SampleNamespaceModelFactory
{
// Previous contract shipped a factory overload for the abstract AbstractModel with fewer
// parameters (before prop2 was added). It must be restored as a hidden back-compat overload
// even though the current factory instantiates the Unknown* derived type.
public static AbstractModel AbstractModel(string prop1 = default, string kind = default)
{ }
}
}

namespace Sample.Models
{
public abstract partial class AbstractModel
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// <auto-generated/>

#nullable disable

using System.ComponentModel;
using Sample.Models;

namespace Sample.Namespace
{
public static partial class SampleNamespaceModelFactory
{
public static global::Sample.Models.AbstractModel AbstractModel(string kind = default, string prop1 = default)
{
return new global::Sample.Models.UnknownAbstractModel(kind, prop1, additionalBinaryDataProperties: null);
}

public static global::Sample.Models.DerivedModel DerivedModel(string prop1 = default, string stringProperty = default)
{
return new global::Sample.Models.DerivedModel("derived", prop1, additionalBinaryDataProperties: null, stringProperty);
}

[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::Sample.Models.UnknownAbstractModel UnknownAbstractModel(string prop1, string kind)
{
return new global::Sample.Models.UnknownAbstractModel(kind, prop1, additionalBinaryDataProperties: null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Sample.Models;

namespace Sample.Namespace
{
public static partial class SampleNamespaceModelFactory
{
// Previous contract shipped a factory whose return type was the Unknown* discriminator model itself,
// rather than the abstract base. It must be restored as a hidden back-compat overload.
public static UnknownAbstractModel UnknownAbstractModel(string prop1 = default, string kind = default)
{ }
}
}

namespace Sample.Models
{
public partial class UnknownAbstractModel
{ }
}
Loading