Skip to content
Draft
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 @@ -412,8 +412,8 @@ public async Task CustomizedPublicConstructorKeepsNestedServiceVersionPublic()
Assert.IsNotNull(clientOptionsProvider.CustomCodeView);
var customConstructor = clientOptionsProvider.CustomCodeView!.Constructors.Single();
var customServiceVersionType = customConstructor.Signature.Parameters.Single().Type;
Assert.IsEmpty(customServiceVersionType.Namespace);
Assert.IsNull(customServiceVersionType.DeclaringType);
Assert.AreEqual(serviceVersionProvider.Type, customServiceVersionType);
Assert.AreEqual(clientOptionsProvider.Type, customServiceVersionType.DeclaringType);

ProviderReferenceMapAnalyzer.ApplyPreWriteAccessibility(mockGenerator.Object.OutputLibrary.TypeProviders);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ public async Task CanReplaceStructMethod(bool isStructCustomized)
Assert.AreEqual(2, customMethodParams.Count);
Assert.AreEqual("p1", customMethodParams[0].Name);
Assert.AreEqual("MyStruct", customMethodParams[0].Type.Name);
Assert.AreEqual(isStructCustomized ? "Sample.TestClient" : string.Empty, customMethodParams[0].Type.Namespace);
Assert.AreEqual("Sample.TestClient", customMethodParams[0].Type.Namespace);

Assert.IsTrue(customMethodParams[0].Type.IsStruct);
Assert.IsTrue(customMethodParams[0].Type.IsNullable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ private static CSharpType ConstructCSharpTypeFromSymbol(
}

string ns = string.Join('.', pieces.Take(pieces.Length - 1));
if (ns.Length == 0 &&
(isNullable ? typeArg : typeSymbol) is INamedTypeSymbol { TypeKind: TypeKind.Error, Arity: 0 } &&
CodeModelGenerator.Instance.TypeFactory.TypeProvidersByName.TryGetValue(name, out var typeProvider))
{
visited.Remove(typeSymbol);
return typeProvider.Type.WithNullable(isNullable);
}

CSharpType? containingType = null;

if (typeSymbol.ContainingType != null && typeSymbol.TypeKind != TypeKind.TypeParameter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ public void SkipInternalModels()
Assert.AreEqual(ModelList.Length - ModelList.Where(m => m.Access == "internal").Count(), modelFactory.Methods.Count);
}

[Test]
public async Task CustomConstructorReferencesUnstubbedTypes()
{
var model = InputFactory.Model("TestModel", properties: []);
var toolConfig = InputFactory.Model("ToolConfig", properties: []);
var executionType = InputFactory.StringEnum("ExecutionType", [("Server", "server")]);
var generator = (await MockHelpers.LoadMockGeneratorAsync(
inputModelTypes: [model, toolConfig],
inputEnumTypes: [executionType],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync())).Object;
var modelFactory = generator.OutputLibrary.TypeProviders.OfType<ModelFactoryProvider>().Single();

var content = new TypeProviderWriter(modelFactory).Write().Content;

Assert.AreEqual(Helpers.GetExpectedFromFile(), content);
}

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

#nullable disable

using System.Collections.Generic;
using Sample.Models;

namespace Sample
{
public static partial class SampleModelFactory
{
public static global::Sample.Models.TestModel TestModel(global::System.Collections.Generic.IDictionary<string, global::Sample.Models.ToolConfig> toolConfigs = default, global::Sample.Models.ExecutionType executionType = default, global::Sample.Models.ExecutionType? optionalExecutionType = default)
{
toolConfigs ??= new global::Sample.ChangeTrackingDictionary<string, global::Sample.Models.ToolConfig>();

return new global::Sample.Models.TestModel(toolConfigs, executionType, optionalExecutionType, default);
}

public static global::Sample.Models.ToolConfig ToolConfig()
{
return new global::Sample.Models.ToolConfig(additionalBinaryDataProperties: null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using Microsoft.TypeSpec.Generator.Customizations;

namespace Sample.Models
{
[CodeGenSuppress("TestModel", typeof(IDictionary<string, BinaryData>))]
public partial class TestModel
{
internal TestModel(IDictionary<string, ToolConfig> toolConfigs, ExecutionType executionType, ExecutionType? optionalExecutionType, IDictionary<string, BinaryData> additionalBinaryDataProperties)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;

namespace Sample
{
public class Container
{
public Model Model { get; }
public Model? NullableModel { get; }
public FixedEnum FixedEnum { get; }
public FixedEnum? NullableFixedEnum { get; }
public ExtensibleEnum ExtensibleEnum { get; }
public ExtensibleEnum? NullableExtensibleEnum { get; }
public IDictionary<Model, IList<Model>> Dictionary { get; }
public Missing Missing { get; }
public Other.Model Qualified { get; }
public Resolved.Model Resolved { get; }
public GlobalModel Global { get; }
public Model<string> Generic { get; }
}
}

namespace Resolved
{
public class Model { }
}

namespace Other
{
public class Container { }
}

public class GlobalModel { }
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,69 @@ private static IPropertySymbol GetPropertySymbol(Compilation compilation, string
Assert.IsNotNull(propertySymbol, $"Failed to resolve property '{propertyName}'.");
return propertySymbol!;
}

[TestCase("Model", false)]
[TestCase("Model", true)]
[TestCase("FixedEnum", false)]
[TestCase("FixedEnum", true)]
[TestCase("ExtensibleEnum", false)]
[TestCase("ExtensibleEnum", true)]
public async Task UnresolvedGeneratedTypes(string name, bool isNullable)
{
var compilation = await Helpers.GetCompilationFromDirectoryAsync();
var generator = MockHelpers.LoadMockGenerator().Object;
CSharpType expected = name == "Model"
? generator.TypeFactory.CreateModel(InputFactory.Model(name))!.Type
: generator.TypeFactory.CreateEnum(InputFactory.StringEnum(
name, [("Value", "value")], isExtensible: name == "ExtensibleEnum"))!.Type;
var symbol = GetPropertySymbol(compilation, "Container", isNullable ? $"Nullable{name}" : name).Type;
var unresolvedSymbol = isNullable ? ((INamedTypeSymbol)symbol).TypeArguments.Single() : symbol;
Assert.AreEqual(TypeKind.Error, unresolvedSymbol.TypeKind);

var type = symbol.GetCSharpType();

Assert.AreEqual(expected.WithNullable(isNullable), type);
Assert.AreEqual(expected.IsEnum, type.IsEnum);
Assert.AreEqual(expected.IsStruct, type.IsStruct);
Assert.AreEqual(expected.IsValueType, type.IsValueType);
Assert.AreEqual(expected.IsPublic, type.IsPublic);
}

[Test]
public async Task UnresolvedGeneratedTypesInGenericArguments()
{
var compilation = await Helpers.GetCompilationFromDirectoryAsync(method: nameof(UnresolvedGeneratedTypes));
var generator = MockHelpers.LoadMockGenerator().Object;
var expected = generator.TypeFactory.CreateModel(InputFactory.Model("Model"))!.Type;
var symbol = GetPropertySymbol(compilation, "Container", "Dictionary").Type;

var type = symbol.GetCSharpType();

Assert.AreEqual(typeof(System.Collections.Generic.IDictionary<,>), type.FrameworkType);
Assert.AreEqual(expected, type.Arguments[0]);
Assert.AreEqual(expected, type.Arguments[1].Arguments[0]);
}

[TestCase("Missing", "Missing", "")]
[TestCase("Qualified", "Model", "Other")]
[TestCase("Resolved", "Model", "Resolved")]
[TestCase("Global", "GlobalModel", "")]
[TestCase("Generic", "Model", "")]
public async Task OtherTypesDoNotResolveByGeneratedName(string property, string name, string expectedNamespace)
{
var compilation = await Helpers.GetCompilationFromDirectoryAsync(method: nameof(UnresolvedGeneratedTypes));
var generator = MockHelpers.LoadMockGenerator().Object;
generator.TypeFactory.CreateModel(InputFactory.Model("Model"));
generator.TypeFactory.CreateModel(InputFactory.Model("GlobalModel"));

var type = GetPropertySymbol(compilation, "Container", property).Type.GetCSharpType();

Assert.AreEqual(name, type.Name);
Assert.AreEqual(expectedNamespace, type.Namespace);
if (property == "Generic")
{
Assert.AreEqual(1, type.Arguments.Count);
}
}
}
}