Add support for implicit and explicit operators (#51)
* . * xxxxxxxx * rev * .... * . * ok * 2 * . * ,
This commit is contained in:
@@ -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)
|
||||
|
||||
+2
@@ -132,6 +132,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.AkkaActor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LocalActorRefProviderProxy(Akka.Actor.LocalActorRefProvider instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -48,6 +48,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public ClientContextProxy(Microsoft.SharePoint.Client.ClientContext instance) : base(instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -85,6 +85,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public ClientObjectProxy(Microsoft.SharePoint.Client.ClientObject instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -143,6 +143,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public ClientRuntimeContextProxy(Microsoft.SharePoint.Client.ClientRuntimeContext instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -46,6 +46,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source.PnP
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public SecurableObjectProxy(Microsoft.SharePoint.Client.SecurableObject instance) : base(instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -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;
|
||||
|
||||
+2
@@ -30,6 +30,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public GenericProxy(ProxyInterfaceSourceGeneratorTests.Source.Generic<T> instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -27,6 +27,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public HumanProxy(ProxyInterfaceSourceGeneratorTests.Source.Human instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+30
@@ -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
|
||||
+2
@@ -25,6 +25,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public MixedVisibilityProxy(ProxyInterfaceSourceGeneratorTests.Source.MixedVisibility instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+61
@@ -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
|
||||
+2
@@ -114,6 +114,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public PersonExtendsProxy(ProxyInterfaceSourceGeneratorTests.Source.PersonExtends instance)
|
||||
{
|
||||
_Instance = instance;
|
||||
|
||||
+2
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user