Add support for implicit and explicit operators (#51)

* .

* xxxxxxxx

* rev

* ....

* .

* ok

* 2

* .

* ,
This commit is contained in:
Stef Heyenrath
2023-01-09 21:06:30 +01:00
committed by GitHub
parent da40aae293
commit 106385a466
21 changed files with 288 additions and 2 deletions
@@ -154,7 +154,7 @@ using System;
{
str.AppendLine($" {attribute}");
}
str.AppendLine($" event {type} {@event.Key.GetSanitizedName()};");
str.AppendLine();
}
@@ -19,6 +19,7 @@ internal partial class ProxyClassesGenerator
str.AppendLine($" Mapster.TypeAdapterConfig<{replacedType.Key}, {replacedType.Value}>.NewConfig().ConstructUsing({instance} => new {classNameProxy}({instance}));");
str.AppendLine($" Mapster.TypeAdapterConfig<{replacedType.Value}, {replacedType.Key}>.NewConfig().MapWith({proxy} => (({classNameProxy}) {proxy})._Instance);");
str.AppendLine();
}
@@ -78,6 +78,7 @@ internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
var properties = GeneratePublicProperties(targetClassSymbol, pd.ProxyBaseClasses);
var methods = GeneratePublicMethods(targetClassSymbol, pd.ProxyBaseClasses);
var events = GenerateEvents(targetClassSymbol, pd.ProxyBaseClasses);
var operators = GenerateOperators(targetClassSymbol, pd.ProxyBaseClasses);
var configurationForMapster = string.Empty;
if (Context.ReplacedTypes.Any())
@@ -111,6 +112,8 @@ using System;
{events}
{operators}
public {constructorName}({targetClassSymbol} instance){@base}
{{
_Instance = instance;
@@ -328,4 +331,43 @@ using System;
return str.ToString();
}
private string GenerateOperators(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
{
var str = new StringBuilder();
foreach (var @operator in MemberHelper.GetPublicStaticOperators(targetClassSymbol, proxyBaseClasses))
{
foreach (var attribute in @operator.GetAttributesAsList())
{
str.AppendLine($" {attribute}");
}
var parameter = @operator.Parameters.First();
var proxyClassName = targetClassSymbol.Symbol.ResolveProxyClassName();
var operatorType = @operator.Name.ToLowerInvariant().Replace("op_", string.Empty);
if (operatorType == "explicit")
{
var returnTypeAsString = GetReplacedType(@operator.ReturnType, out _);
str.AppendLine($" public static explicit operator {returnTypeAsString}({proxyClassName} {parameter.Name})");
str.AppendLine(@" {");
str.AppendLine($" return ({returnTypeAsString}) {parameter.Name}._Instance;");
str.AppendLine(@" }");
}
else
{
var returnTypeAsString = GetReplacedType(parameter.Type, out _);
str.AppendLine($" public static implicit operator {proxyClassName}({returnTypeAsString} {parameter.Name})");
str.AppendLine(@" {");
str.AppendLine($" return new {proxyClassName}(({targetClassSymbol.Symbol.Name}) {parameter.Name});");
str.AppendLine(@" }");
}
str.AppendLine();
}
return str.ToString();
}
}
@@ -38,6 +38,24 @@ internal static class MemberHelper
.ToArray();
}
public static IReadOnlyList<IMethodSymbol> GetPublicStaticOperators(
ClassSymbol classSymbol,
bool proxyBaseClasses,
Func<IMethodSymbol, bool>? filter = null)
{
filter ??= _ => true;
return
GetPublicMembers(
classSymbol,
proxyBaseClasses,
m => m.Kind == SymbolKind.Method,
m => m.MethodKind == MethodKind.Conversion,
m => !ExcludedMethods.Contains(m.Name),
filter)
.ToArray();
}
public static IReadOnlyList<IGrouping<ISymbol, IMethodSymbol>> GetPublicEvents(
ClassSymbol classSymbol,
bool proxyBaseClasses,
@@ -47,7 +65,8 @@ internal static class MemberHelper
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
#pragma warning disable RS1024 // Compare symbols correctly
return GetPublicMembers(classSymbol,
return GetPublicMembers(
classSymbol,
proxyBaseClasses,
m => m.MethodKind is MethodKind.EventAdd or MethodKind.EventRemove/* || m.MethodKind == MethodKind.EventRaise*/,
filter)
@@ -132,6 +132,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.AkkaActor
public LocalActorRefProviderProxy(Akka.Actor.LocalActorRefProvider instance)
{
_Instance = instance;
@@ -48,6 +48,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
public ClientContextProxy(Microsoft.SharePoint.Client.ClientContext instance) : base(instance)
{
_Instance = instance;
@@ -85,6 +85,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
public ClientObjectProxy(Microsoft.SharePoint.Client.ClientObject instance)
{
_Instance = instance;
@@ -143,6 +143,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
public ClientRuntimeContextProxy(Microsoft.SharePoint.Client.ClientRuntimeContext instance)
{
_Instance = instance;
@@ -46,6 +46,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
public SecurableObjectProxy(Microsoft.SharePoint.Client.SecurableObject instance) : base(instance)
{
_Instance = instance;
@@ -1131,6 +1131,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
public WebProxy(Microsoft.SharePoint.Client.Web instance) : base(instance)
{
_Instance = instance;
@@ -24,6 +24,8 @@ using System;
public NoNamespaceProxy(ProxyInterfaceSourceGeneratorTests.Source.NoNamespace instance)
{
_Instance = instance;
@@ -30,6 +30,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
public GenericProxy(ProxyInterfaceSourceGeneratorTests.Source.Generic<T> instance)
{
_Instance = instance;
@@ -27,6 +27,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
public HumanProxy(ProxyInterfaceSourceGeneratorTests.Source.Human instance)
{
_Instance = instance;
@@ -0,0 +1,30 @@
//----------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//----------------------------------------------------------------------------------------
#nullable enable
using System;
namespace ProxyInterfaceSourceGeneratorTests.Source
{
public partial interface IOperatorTest
{
ProxyInterfaceSourceGeneratorTests.Source.OperatorTest _Instance { get; }
string Name { get; set; }
int? Id { get; set; }
}
}
#nullable disable
@@ -25,6 +25,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
public MixedVisibilityProxy(ProxyInterfaceSourceGeneratorTests.Source.MixedVisibility instance)
{
_Instance = instance;
@@ -0,0 +1,61 @@
//----------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//----------------------------------------------------------------------------------------
#nullable enable
using System;
namespace ProxyInterfaceSourceGeneratorTests.Source
{
public partial class OperatorTestProxy : IOperatorTest
{
public ProxyInterfaceSourceGeneratorTests.Source.OperatorTest _Instance { get; }
public string Name { get => _Instance.Name; set => _Instance.Name = value; }
public int? Id { get => _Instance.Id; set => _Instance.Id = value; }
public static implicit operator OperatorTestProxy(string name)
{
return new OperatorTestProxy((OperatorTest) name);
}
public static implicit operator OperatorTestProxy(int? id)
{
return new OperatorTestProxy((OperatorTest) id);
}
public static explicit operator string(OperatorTestProxy test)
{
return (string) test._Instance;
}
public static explicit operator int?(OperatorTestProxy test)
{
return (int?) test._Instance;
}
public OperatorTestProxy(ProxyInterfaceSourceGeneratorTests.Source.OperatorTest instance)
{
_Instance = instance;
}
}
}
#nullable disable
@@ -114,6 +114,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
public PersonExtendsProxy(ProxyInterfaceSourceGeneratorTests.Source.PersonExtends instance)
{
_Instance = instance;
@@ -168,6 +168,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
public PersonProxy(ProxyInterfaceSourceGeneratorTests.Source.Person instance) : base(instance)
{
_Instance = instance;
@@ -88,6 +88,63 @@ namespace ProxyInterfaceSourceGeneratorTests
}
}
[Fact]
public void GenerateFiles_ForClassWithOperator_Should_GenerateCorrectFiles()
{
// Arrange
var fileNames = new[]
{
"ProxyInterfaceSourceGeneratorTests.Source.IOperatorTest.g.cs",
"ProxyInterfaceSourceGeneratorTests.Source.OperatorTestProxy.g.cs"
};
var path = "./Source/IOperatorTest.cs";
var sourceFile = new SourceFile
{
Path = path,
Text = File.ReadAllText(path),
AttributeToAddToInterface = new ExtraAttribute
{
Name = "ProxyInterfaceGenerator.Proxy",
ArgumentList = "typeof(ProxyInterfaceSourceGeneratorTests.Source.OperatorTest)"
}
};
// Act
var result = _sut.Execute(new[]
{
sourceFile
});
// Assert
result.Valid.Should().BeTrue();
result.Files.Should().HaveCount(fileNames.Length + 1);
foreach (var fileName in fileNames.Select((fileName, index) => new { fileName, index }))
{
var builder = result.Files[fileName.index + 1]; // +1 means skip the attribute
builder.Path.Should().EndWith(fileName.fileName);
if (Write) File.WriteAllText($"../../../Destination/{fileName.fileName}", builder.Text);
builder.Text.Should().Be(File.ReadAllText($"../../../Destination/{fileName.fileName}"));
}
var name = "stef";
var operatorTest = new OperatorTest
{
Name = name
};
string name1 = (string) operatorTest;
name1.Should().Be(name);
var p = new OperatorTestProxy(operatorTest);
string name2 = (string)p;
name2.Should().Be(name);
var p2 = (OperatorTestProxy)name;
p2.Should().BeEquivalentTo(new OperatorTestProxy(operatorTest));
}
[Fact]
public void GenerateFiles_When_NoNamespace_Should_GenerateCorrectFiles()
{
@@ -0,0 +1,6 @@
// file-scoped namespace !
namespace ProxyInterfaceSourceGeneratorTests.Source;
public partial interface IOperatorTest
{
}
@@ -0,0 +1,46 @@
namespace ProxyInterfaceSourceGeneratorTests.Source
{
public class OperatorTest
{
public string Name { get; set; } = null!;
public int? Id { get; set; }
// Operator : implicit
public static implicit operator OperatorTest(string name)
{
return new()
{
Name = name
};
}
public static implicit operator OperatorTest(int? id)
{
return new()
{
Id = id
};
}
// Operator : explicit
public static explicit operator string(OperatorTest test)
{
return test.Name;
}
public static explicit operator int?(OperatorTest test)
{
return test.Id;
}
}
public class X
{
public X()
{
OperatorTest operatorTest = "stef";
var s = (string)operatorTest;
}
}
}