Skip to content
Closed
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 @@ -450,18 +450,6 @@ private string BuildNamespaceCore()
return ns;
}

internal static (string Name, string Namespace, string OriginalName) GetNameForCollisionCheck(InputClient inputClient)
{
// Constructing a client here can resolve parameter models and reenter the model-name index.
var typeNamespace = string.IsNullOrEmpty(inputClient.Namespace)
? ScmCodeModelGenerator.Instance.TypeFactory.PrimaryNamespace
: ScmCodeModelGenerator.Instance.TypeFactory.GetCleanNameSpace(inputClient.Namespace);
var name = inputClient.IsExactName ? inputClient.Name : NormalizeTypeNameForNewContract(
inputClient.Name.ToIdentifierName(), typeNamespace, null, static name => name.NormalizeCSharpAcronyms());
var customType = ScmCodeModelGenerator.Instance.SourceInputModel.FindForTypeInCurrentCompilation(typeNamespace, name);
return (customType?.Name ?? name, customType?.Type.Namespace ?? typeNamespace, name);
}

/// <summary>
/// Checks if two namespaces share the same last segment
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ public class ScmTypeFactory : TypeFactory
private Dictionary<InputClient, ClientProvider?>? _clientCache;
private Dictionary<InputClient, ClientProvider?> ClientCache => _clientCache ??= [];

protected override IEnumerable<(string Name, string Namespace, string OriginalName)> GetReservedModelTypeNames()
=> ScmCodeModelGenerator.Instance.InputLibrary.InputNamespace.Clients.Select(ClientProvider.GetNameForCollisionCheck);

public virtual CSharpType MatchConditionsType => typeof(PipelineMessageClassifier);

public virtual IClientResponseApi ClientResponseApi => ClientResultProvider.Instance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,85 +25,6 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.ScmModelProvi
{
public class ScmModelProviderTests
{
[TestCase(InputModelTypeUsage.Input, "WidgetRequest")]
[TestCase(InputModelTypeUsage.Output, "WidgetContent")]
[TestCase(InputModelTypeUsage.Input | InputModelTypeUsage.Output, "WidgetContent")]
public void ContentSuffixUsageIsReflectedInGeneratedCode(InputModelTypeUsage usage, string expectedName)
{
var input = InputFactory.Model("WidgetContent",
usage: usage | InputModelTypeUsage.Json,
properties: [InputFactory.Property("serviceContent", InputPrimitiveType.String)]);
MockHelpers.LoadMockGenerator(inputModels: () => [input]);
var factory = ScmCodeModelGenerator.Instance.TypeFactory;
var model = factory.CreateModel(input)!;
var code = new TypeProviderWriter(model).Write().Content;

Assert.AreEqual(Helpers.GetExpectedFromFile(usage.ToString()).ReplaceLineEndings("\n"), code);
Assert.AreEqual($"{expectedName}.cs", System.IO.Path.GetFileName(model.RelativeFilePath));
Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
}

[TestCase("WidgetRequest", "Sample.Models", true, "WidgetContent", false)]
[TestCase("WidgetRequest", "Sample.Models", true, "WidgetContent", true)]
[TestCase("WidgetRequest", "Other.Models", true, "WidgetRequest", false)]
[TestCase("WidgetRequest", "Other.Models", true, "WidgetRequest", true)]
[TestCase("IpRequest", "Sample.Models", false, "IPContent", false)]
[TestCase("IpRequest", "Sample.Models", true, "IPRequest", true)]
public void ModelSuffixDoesNotCollideWithClient(
string clientName, string clientNamespace, bool exactName, string expectedName, bool clientFirst)
{
var input = InputFactory.Model(clientName.StartsWith("Ip", StringComparison.Ordinal) ? "IpContent" : "WidgetContent",
usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
var inputClient = InputFactory.Client(clientName, clientNamespace: clientNamespace, isExactName: exactName);
MockHelpers.LoadMockGenerator(inputModels: () => [input], clients: () => [inputClient]);
var factory = ScmCodeModelGenerator.Instance.TypeFactory;
ClientProvider? client = clientFirst ? factory.CreateClient(inputClient) : null;
var model = factory.CreateModel(input)!;
client ??= factory.CreateClient(inputClient)!;

Assert.AreEqual(expectedName, model.Name);
Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
Assert.AreSame(client, factory.CSharpTypeMap[client.Type]);
Assert.AreNotEqual(model.Type, client.Type);
}

[TestCase("GadgetClient", "WidgetResponse", "WidgetResponse", "WidgetResult")]
[TestCase("WidgetRequest", "WidgetContent", "WidgetContent", "CustomizedRequest")]
public async Task ModelSuffixDoesNotCollideWithCustomizedClient(
string clientName, string modelName, string expectedName, string expectedClientName)
{
var input = InputFactory.Model(modelName, usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
var inputClient = InputFactory.Client(clientName, clientNamespace: "Sample.Models", isExactName: true);
await MockHelpers.LoadMockGeneratorAsync(
inputModels: () => [input],
clients: () => [inputClient],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
var factory = ScmCodeModelGenerator.Instance.TypeFactory;
var model = factory.CreateModel(input)!;
var client = factory.CreateClient(inputClient)!;

Assert.AreEqual(expectedName, model.Name);
Assert.AreEqual(expectedClientName, client.Name);
Assert.IsNull(model.CustomCodeView);
Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
Assert.AreSame(client, factory.CSharpTypeMap[client.Type]);
}

[Test]
public void ModelSuffixFallbackDoesNotCollideWithClient()
{
var input = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
MockHelpers.LoadMockGenerator(inputModels: () => [input], clients: () =>
[
InputFactory.Client("WidgetRequest", clientNamespace: "Sample.Models", isExactName: true),
InputFactory.Client("WidgetContent", clientNamespace: "Sample.Models", isExactName: true)
]);

var model = ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(input)!;

Assert.AreEqual("WidgetContent1", model.Name);
}

[TestCase(false, "json")]
[TestCase(true, "json")]
[TestCase(false, "xml")]
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ namespace Microsoft.TypeSpec.Generator.Providers
public class ModelProvider : TypeProvider
{
private const string AdditionalBinaryDataPropsFieldDescription = "Keeps track of any properties unknown to the library.";
private const string ContentSuffix = "Content";
private const string ResponseSuffix = "Response";
private readonly InputModelType _inputModel;
internal InputModelType InputModel => _inputModel;
Expand Down Expand Up @@ -433,25 +432,6 @@ protected override TypeProvider[] BuildSerializationProviders()
protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Models", $"{Name}.cs");

protected override string BuildName()
{
return CodeModelGenerator.Instance.TypeFactory.ModelNames.TryGetValue(_inputModel, out var name)
? name
: BuildNameWithoutCollisionCheck();
}

internal (string Name, string FinalName, string OriginalName, string Namespace, string FinalNamespace, bool CanRename, bool HasTypeMapping) GetNameForCollisionCheck()
{
var name = BuildNameWithoutCollisionCheck();
var originalName = _inputModel.IsExactName ? _inputModel.Name : _inputModel.Name.ToIdentifierName().NormalizeCSharpAcronyms();
var typeNamespace = BuildNamespace();
var customType = base.BuildCustomCodeView(name, typeNamespace);
var canRename = !_inputModel.IsExactName && name != originalName &&
name == RenameModelSuffix(originalName);
return (name, customType?.Name ?? name, originalName, typeNamespace, customType?.Type.Namespace ?? typeNamespace,
canRename, CodeModelGenerator.Instance.SourceInputModel.HasTypeMapping(typeNamespace, name, DeclaringTypeName));
}

private string BuildNameWithoutCollisionCheck()
{
if (_inputModel.IsExactName)
{
Expand All @@ -464,8 +444,8 @@ private string BuildNameWithoutCollisionCheck()
private protected override string NormalizeTypeName(string name)
{
var normalizedName = base.NormalizeTypeName(name);
var renamedName = RenameModelSuffix(normalizedName);
if (renamedName == normalizedName)
if (_inputModel.Usage.HasFlag(InputModelTypeUsage.Error) ||
!normalizedName.EndsWith(ResponseSuffix, StringComparison.Ordinal))
{
return normalizedName;
}
Expand All @@ -478,32 +458,15 @@ private protected override string NormalizeTypeName(string name)
return normalizedName;
}

return renamedName;
}

private string RenameModelSuffix(string name)
{
if ((_inputModel.Usage & (InputModelTypeUsage.Input | InputModelTypeUsage.Output | InputModelTypeUsage.Error)) == InputModelTypeUsage.Input &&
name.EndsWith(ContentSuffix, StringComparison.Ordinal))
{
return $"{name[..^ContentSuffix.Length]}Request";
}

if (!_inputModel.Usage.HasFlag(InputModelTypeUsage.Error) &&
name.EndsWith(ResponseSuffix, StringComparison.Ordinal))
{
return $"{name[..^ResponseSuffix.Length]}Result";
}

return name;
return $"{normalizedName[..^ResponseSuffix.Length]}Result";
}

private protected override TypeProvider? BuildCustomCodeView(string? generatedTypeName = null, string? generatedTypeNamespace = null)
{
var typeNamespace = generatedTypeNamespace ?? BuildNamespace();
var typeName = generatedTypeName ?? BuildName();
var customCodeView = base.BuildCustomCodeView(typeName, typeNamespace);
return customCodeView ?? BuildModelSuffixFallbackView(
return customCodeView ?? BuildResponseSuffixFallbackView(
typeName,
typeNamespace,
(name, ns) => base.BuildCustomCodeView(name, ns));
Expand All @@ -514,13 +477,13 @@ private string RenameModelSuffix(string name)
var typeNamespace = generatedTypeNamespace ?? CustomCodeView?.Type.Namespace ?? BuildNamespace();
var typeName = generatedTypeName ?? CustomCodeView?.Name ?? BuildName();
var lastContractView = base.BuildLastContractView(typeName, typeNamespace);
return lastContractView ?? BuildModelSuffixFallbackView(
return lastContractView ?? BuildResponseSuffixFallbackView(
typeName,
typeNamespace,
(name, ns) => base.BuildLastContractView(name, ns));
}

private TypeProvider? BuildModelSuffixFallbackView(
private TypeProvider? BuildResponseSuffixFallbackView(
string typeName,
string typeNamespace,
Func<string, string, TypeProvider?> buildView)
Expand All @@ -532,10 +495,9 @@ private string RenameModelSuffix(string name)

var originalName = _inputModel.Name.ToIdentifierName();
var normalizedOriginalName = originalName.NormalizeCSharpAcronyms();
var renamedName = RenameModelSuffix(normalizedOriginalName);
if (renamedName == normalizedOriginalName ||
if (!normalizedOriginalName.EndsWith(ResponseSuffix, StringComparison.Ordinal) ||
originalName == typeName ||
typeName != renamedName)
typeName != $"{normalizedOriginalName[..^ResponseSuffix.Length]}Result")
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -849,36 +849,33 @@ protected virtual XmlDocProvider BuildXmlDocs()
protected virtual string? BuildOriginalName() => _inputType is { IsExactName: false } ? _inputType.Name.ToIdentifierName() : null;

protected string NormalizeTypeNameForNewContract(string name)
=> NormalizeTypeNameForNewContract(name, BuildNamespace(), _declaringTypeName.Value, NormalizeTypeName);

protected static string NormalizeTypeNameForNewContract(
string name, string typeNamespace, string? declaringTypeName, Func<string, string> normalizeTypeName)
{
var typeNamespace = BuildNamespace();
var currentType = CodeModelGenerator.Instance.SourceInputModel.FindForTypeInCurrentCompilation(
typeNamespace,
name,
declaringTypeName);
_declaringTypeName.Value);
if (currentType is not null)
{
return name;
}

var normalizedName = normalizeTypeName(name);
var normalizedName = NormalizeTypeName(name);
if (normalizedName == name)
{
return name;
}

if (CodeModelGenerator.Instance.SourceInputModel.FindForTypeInCurrentCompilation(
typeNamespace, normalizedName, declaringTypeName) is not null)
typeNamespace, normalizedName, _declaringTypeName.Value) is not null)
{
return normalizedName;
}

var lastContractType = CodeModelGenerator.Instance.SourceInputModel.FindForTypeInLastContract(
typeNamespace,
name,
declaringTypeName);
_declaringTypeName.Value);
return lastContractType is null ? normalizedName : name;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ private IReadOnlyDictionary<string, INamedTypeSymbol> PopulateNameMap()
return FindTypeInCompilation(Customization, ns, name, includeReferencedAssemblies, declaringTypeName);
}

internal bool HasTypeMapping(string ns, string name, string? declaringTypeName = null)
=> _nameMap.Value.TryGetValue(name, out var type) && IsContainingTypeMatch(type, ns, declaringTypeName);

public TypeProvider? FindForTypeInLastContract(string ns, string name, string? declaringTypeName = null)
{
var type = FindTypeInCompilation(LastContract, ns, name, true, declaringTypeName, includeInternal: false);
Expand Down
Loading
Loading