Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement the feature of changing the name of a model #4285

Merged
merged 20 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -7,6 +7,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.SourceInput;

namespace Microsoft.Generator.CSharp
{
Expand All @@ -29,6 +30,8 @@ public async Task ExecuteAsync()
var generatedTestOutputPath = Path.Combine(outputPath, "..", "..", "tests", GeneratedFolderName);

GeneratedCodeWorkspace workspace = await GeneratedCodeWorkspace.Create();
GeneratedCodeWorkspace existingCode = GeneratedCodeWorkspace.CreateExistingCodeProject(CodeModelPlugin.Instance.Configuration.ProjectDirectory, generatedSourceOutputPath);
SourceInputModel.Initialize(await existingCode.GetCompilationAsync());

var output = CodeModelPlugin.Instance.OutputLibrary;
Directory.CreateDirectory(Path.Combine(generatedSourceOutputPath, "Models"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public static void Initialize()
_cachedProject = Task.Run(CreateGeneratedCodeProject);
}

internal async Task<CSharpCompilation> GetCompilationAsync()
{
var compilation = await _project.GetCompilationAsync() as CSharpCompilation;
Debug.Assert(compilation != null);

return compilation;
}

public void AddPlainFiles(string name, string content)
{
PlainFiles.Add(name, content);
Expand Down Expand Up @@ -140,17 +148,18 @@ internal static async Task<GeneratedCodeWorkspace> Create()
return new GeneratedCodeWorkspace(generatedCodeProject);
}

public static GeneratedCodeWorkspace CreateExistingCodeProject(string outputDirectory)
internal static GeneratedCodeWorkspace CreateExistingCodeProject(string projectDirectory, string generatedDirectory)
{
var workspace = new AdhocWorkspace();
var newOptionSet = workspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, _newLine);
workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(newOptionSet));
Project project = workspace.AddProject("ExistingCode", LanguageNames.CSharp);

if (Path.IsPathRooted(outputDirectory))
if (Path.IsPathRooted(projectDirectory))
{
outputDirectory = Path.GetFullPath(outputDirectory);
project = AddDirectory(project, outputDirectory, null);
projectDirectory = Path.GetFullPath(projectDirectory);

project = AddDirectory(project, projectDirectory, skipPredicate: sourceFile => sourceFile.StartsWith(generatedDirectory));
}

project = project
Expand All @@ -161,7 +170,7 @@ public static GeneratedCodeWorkspace CreateExistingCodeProject(string outputDire
return new GeneratedCodeWorkspace(project);
}

public static async Task<Compilation?> CreatePreviousContractFromDll(string xmlDocumentationpath, string dllPath)
internal static async Task<Compilation?> CreatePreviousContractFromDll(string xmlDocumentationpath, string dllPath)
{
var workspace = new AdhocWorkspace();
Project project = workspace.AddProject("PreviousContract", LanguageNames.CSharp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
using System.Linq;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Statements;

namespace Microsoft.Generator.CSharp.Providers
{
public class NamedTypeSymbolProvider : TypeProvider
public sealed class NamedTypeSymbolProvider : TypeProvider
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved
{
private INamedTypeSymbol _namedTypeSymbol;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,29 @@
using System.Linq;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.SourceInput;
using Microsoft.Generator.CSharp.Statements;

namespace Microsoft.Generator.CSharp.Providers
{
public abstract class TypeProvider
{
private Lazy<NamedTypeSymbolProvider?> _customization;
protected TypeProvider()
{
_customization = new(GetCustomizationProvider);
}

private NamedTypeSymbolProvider? GetCustomizationProvider()
{
if (this is NamedTypeSymbolProvider)
return null;
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved
var type = SourceInputModel.Instance.FindForType(GetNamespace(), BuildName());
return type != null ? new NamedTypeSymbolProvider(type) : null;
}

public NamedTypeSymbolProvider? Customization => _customization.Value;
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved

protected string? _deprecated;

/// <summary>
Expand All @@ -22,10 +39,13 @@ public abstract class TypeProvider

private string? _relativeFilePath;

public string Name => _name ??= BuildName();
public string Name => _name ??= GetName();

private string? _name;

private string GetName()
=> Customization?.Name ?? BuildName();
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved

protected virtual FormattableString Description { get; } = FormattableStringHelpers.Empty;

private XmlDocProvider? _xmlDocs;
Expand All @@ -45,7 +65,7 @@ public string? Deprecated
private CSharpType? _type;
public CSharpType Type => _type ??= new(
this,
GetNamespace(),
Customization?.GetNamespace() ?? GetNamespace(),
GetTypeArguments(),
GetBaseType());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,28 @@
using Microsoft.CodeAnalysis;
using Microsoft.Generator.CSharp.Customization;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Providers;
using NuGet.Configuration;

namespace Microsoft.Generator.CSharp.SourceInput
{
internal sealed class SourceInputModel
internal class SourceInputModel
{
private static SourceInputModel? _instance;
public static SourceInputModel Instance => _instance ?? throw new InvalidOperationException("SourceInputModel has not been initialized");
public static SourceInputModel Instance
{
get
{
return _instance ?? throw new InvalidOperationException("SourceInputModel has not been initialized");
}
set
{
_instance = value;
}
}

public static void Initialize(Compilation customization, CompilationCustomCode? existingCompilation = null)
public static void Initialize(Compilation customization, CompilationCustomCode? previousContract = null)
{
_instance = new SourceInputModel(customization, existingCompilation);
_instance = new SourceInputModel(customization, previousContract);
}

private readonly CompilationCustomCode? _existingCompilation;
Expand All @@ -33,7 +42,7 @@ public static void Initialize(Compilation customization, CompilationCustomCode?
public Compilation Customization { get; }
public Compilation? PreviousContract { get; }

private SourceInputModel(Compilation customization, CompilationCustomCode? existingCompilation = null)
internal SourceInputModel(Compilation customization, CompilationCustomCode? existingCompilation = null)
{
Customization = customization;
PreviousContract = LoadBaselineContract().GetAwaiter().GetResult();
Expand Down Expand Up @@ -77,9 +86,8 @@ private SourceInputModel(Compilation customization, CompilationCustomCode? exist
return _existingCompilation?.FindMethod(namespaceName, typeName, methodName, parameters);
}

public INamedTypeSymbol? FindForType(string name)
public INamedTypeSymbol? FindForType(string ns, string name)
{
var ns = CodeModelPlugin.Instance.Configuration.ModelNamespace;
var fullyQualifiedMetadataName = $"{ns}.{name}";
if (!_nameMap.TryGetValue(name, out var type) &&
!_nameMap.TryGetValue(fullyQualifiedMetadataName, out type))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Tests.Common;
using NUnit.Framework;

namespace Microsoft.Generator.CSharp.Tests.Providers.ModelProviders
{
public class ModelCustomizationTests
{
// Validates that the property body's setter is correctly set based on the property type
[TestCase]
public void TestCustomization_CanChangeModelName()
{
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(_customizationCode);
CSharpCompilation compilation = CSharpCompilation.Create("ExistingCode")
.WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication))
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
.AddSyntaxTrees(syntaxTree);
MockHelpers.LoadMockPlugin(customization: compilation);

var props = new[]
{
InputFactory.Property("prop1", InputFactory.Array(InputPrimitiveType.String))
};

var inputModel = InputFactory.Model("mockInputModel", properties: props);
var modelTypeProvider = new ModelProvider(inputModel);
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved

Assert.AreEqual("CustomizedModel", modelTypeProvider.Type.Name);
Assert.AreEqual("NewNamespace.Models", modelTypeProvider.Type.Namespace);
}

private const string _customizationCode = @"#nullable disable
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved

using System;

namespace NewNamespace
{
[AttributeUsage(AttributeTargets.Class)]
internal class CodeGenTypeAttribute : Attribute
{
public string OriginalName { get; }

public CodeGenTypeAttribute(string originalName)
{
OriginalName = originalName;
}
}
}
namespace NewNamespace.Models
{
[CodeGenType(""MockInputModel"")]
public partial class CustomizedModel
{
}
}
";
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Moq.Protected;
using Moq;
using Microsoft.Generator.CSharp.SourceInput;
using Microsoft.Generator.CSharp.Tests.Common;
using Moq;
using Moq.Protected;

namespace Microsoft.Generator.CSharp.Tests
{
Expand All @@ -20,7 +21,8 @@ public static Mock<CodeModelPlugin> LoadMockPlugin(
Func<InputType, CSharpType>? createCSharpTypeCore = null,
Func<OutputLibrary>? createOutputLibrary = null,
string? configuration = null,
InputModelType[]? inputModelTypes = null)
InputModelType[]? inputModelTypes = null,
Compilation? customization = null)
{
var configFilePath = Path.Combine(AppContext.BaseDirectory, TestHelpersFolder);
// initialize the singleton instance of the plugin
Expand Down Expand Up @@ -48,6 +50,18 @@ public static Mock<CodeModelPlugin> LoadMockPlugin(
mockPlugin.SetupGet(p => p.TypeFactory).Returns(mockTypeFactory.Object);

CodeModelPlugin.Instance = mockPlugin.Object;

if (customization == null)
{
var workspace = new AdhocWorkspace();
Project project = workspace.AddProject("ExistingCode", LanguageNames.CSharp);
customization = project.GetCompilationAsync().GetAwaiter().GetResult()!;
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved
}

var sourceInputModel = new Mock<SourceInputModel>(() => new SourceInputModel(customization, null)) { CallBase = true };

SourceInputModel.Instance = sourceInputModel.Object;

return mockPlugin;
}
}
Expand Down
Loading