From d7483d6b7e7888b88b5d08514ac4f0fc7591be6d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 4 Feb 2022 11:33:26 +0100 Subject: [PATCH] Add support for base (proxy) class (#29) --- .../ProxyInterfaceConsumer/IPersonTT.cs | 9 +- .../ProxyInterfaceConsumer/PersonT.cs | 9 +- .../ProxyInterfaceConsumer/PersonTT.cs | 15 +-- .../ProxyInterfaceConsumer/Program.cs | 110 ++++------------- .../ProxyInterfaceConsumer.csproj | 2 - .../Extensions/NamedTypeSymbolExtensions.cs | 7 ++ .../Extensions/PropertySymbolExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 29 +++++ .../Extensions/TypeSymbolExtensions.cs | 9 +- .../FileGenerators/BaseGenerator.cs | 34 ++--- .../FileGenerators/FileData.cs | 3 - .../FileGenerators/IFileGenerator.cs | 2 + .../FileGenerators/IFilesGenerator.cs | 2 + .../PartialInterfacesGenerator.cs | 32 +++-- .../FileGenerators/ProxyAttributeGenerator.cs | 2 + .../FileGenerators/ProxyClassesGenerator.cs | 116 ++++++++++++------ .../{Model => Models}/ClassSymbol.cs | 2 +- .../{ => Models}/Context.cs | 29 +++-- .../{ => Models}/ContextData.cs | 20 ++- .../Models/FileData.cs | 3 + .../{SyntaxReceiver => Models}/ProxyData.cs | 21 ++-- .../ProxyInterfaceCodeGenerator.cs | 39 +++--- .../ProxyInterfaceSourceGenerator.csproj | 14 ++- .../SyntaxReceiver/ProxySyntaxReceiver.cs | 10 +- .../Utils/MemberHelper.cs | 2 +- ...ourceGeneratorTests.Source.HumanProxy.g.cs | 8 +- ...aceSourceGeneratorTests.Source.IHuman.g.cs | 2 + ...ceSourceGeneratorTests.Source.IPerson.g.cs | 2 + ...eGeneratorTests.Source.IPersonExtends.g.cs | 2 + ...eratorTests.Source.PersonExtendsProxy.g.cs | 38 +++--- ...urceGeneratorTests.Source.PersonProxy.g.cs | 52 +++++--- .../ProxyInterfaceSourceGeneratorTest.cs | 37 +++++- .../ProxyInterfaceSourceGeneratorTests.csproj | 19 +-- .../Source/Human.cs | 4 + .../Source/IMyStruct.cs | 6 + .../Source/MyStruct.cs | 7 ++ .../Source/Person.cs | 8 +- .../Source/PersonExtends.cs | 1 + 38 files changed, 400 insertions(+), 309 deletions(-) create mode 100644 src/ProxyInterfaceSourceGenerator/Extensions/StringExtensions.cs delete mode 100644 src/ProxyInterfaceSourceGenerator/FileGenerators/FileData.cs rename src/ProxyInterfaceSourceGenerator/{Model => Models}/ClassSymbol.cs (83%) rename src/ProxyInterfaceSourceGenerator/{ => Models}/Context.cs (81%) rename src/ProxyInterfaceSourceGenerator/{ => Models}/ContextData.cs (63%) create mode 100644 src/ProxyInterfaceSourceGenerator/Models/FileData.cs rename src/ProxyInterfaceSourceGenerator/{SyntaxReceiver => Models}/ProxyData.cs (57%) create mode 100644 tests/ProxyInterfaceSourceGeneratorTests/Source/IMyStruct.cs create mode 100644 tests/ProxyInterfaceSourceGeneratorTests/Source/MyStruct.cs diff --git a/src-examples/ProxyInterfaceConsumer/IPersonTT.cs b/src-examples/ProxyInterfaceConsumer/IPersonTT.cs index e9c63ee..16df005 100644 --- a/src-examples/ProxyInterfaceConsumer/IPersonTT.cs +++ b/src-examples/ProxyInterfaceConsumer/IPersonTT.cs @@ -1,12 +1,5 @@ -namespace ProxyInterfaceConsumer +namespace ProxyInterfaceConsumer { - [ProxyInterfaceGenerator.Proxy(typeof(ProxyInterfaceConsumer.PersonT<,>))] - public partial interface IPersonT - where T1 : struct - where T2 : class, new() - { - } - [ProxyInterfaceGenerator.Proxy(typeof(ProxyInterfaceConsumer.PersonTT<,>))] public partial interface IPersonTT where T1 : struct diff --git a/src-examples/ProxyInterfaceConsumer/PersonT.cs b/src-examples/ProxyInterfaceConsumer/PersonT.cs index e7fdeef..4e46bf7 100644 --- a/src-examples/ProxyInterfaceConsumer/PersonT.cs +++ b/src-examples/ProxyInterfaceConsumer/PersonT.cs @@ -1,6 +1,6 @@ -namespace ProxyInterfaceConsumer +namespace ProxyInterfaceConsumer { - public class PersonT where T: struct + public class PersonT where T : struct { public T TVal { get; set; } @@ -8,5 +8,10 @@ { return default; } + + public PersonT Call2(int x, PersonT pt) + { + return new PersonT(); + } } } \ No newline at end of file diff --git a/src-examples/ProxyInterfaceConsumer/PersonTT.cs b/src-examples/ProxyInterfaceConsumer/PersonTT.cs index fcd4f69..b474a58 100644 --- a/src-examples/ProxyInterfaceConsumer/PersonTT.cs +++ b/src-examples/ProxyInterfaceConsumer/PersonTT.cs @@ -1,18 +1,5 @@ -namespace ProxyInterfaceConsumer +namespace ProxyInterfaceConsumer { - public class PersonT - where T1 : struct - where T2 : class, new() - { - public T1 TVal1 { get; set; } - - public T2 TVal2 { get; set; } - - public void Call(int x, T1 t1, T2 t2) - { - } - } - public class PersonTT where T1 : struct where T2 : class, new() diff --git a/src-examples/ProxyInterfaceConsumer/Program.cs b/src-examples/ProxyInterfaceConsumer/Program.cs index b10cbb1..3ec831f 100644 --- a/src-examples/ProxyInterfaceConsumer/Program.cs +++ b/src-examples/ProxyInterfaceConsumer/Program.cs @@ -15,10 +15,12 @@ namespace ProxyInterfaceConsumer public static void Main() { - //IPersonT pT = new PersonTProxy(new PersonT()); - //pT.TVal = 1; - //Console.WriteLine(JsonSerializer.Serialize(pT, JsonSerializerOptions)); - //Console.WriteLine(new string('-', 80)); + var t = new TestProxy(new Test()); + + IPersonT pT = new PersonTProxy(new PersonT()); + pT.TVal = 1; + Console.WriteLine(JsonSerializer.Serialize(pT, JsonSerializerOptions)); + Console.WriteLine(new string('-', 80)); //IPersonTT pTT = new PersonTTProxy(new PersonTT()); //pTT.TVal1 = 42; @@ -43,7 +45,8 @@ namespace ProxyInterfaceConsumer Console.WriteLine(JsonSerializer.Serialize(p, JsonSerializerOptions)); } } - public struct Test + + public class Test { public int Id { get; set; } @@ -51,10 +54,20 @@ namespace ProxyInterfaceConsumer public IList Cs { get; set; } - public int Add(string s) + public int AddString(string s) { return 600; } + + public Test AddTest(Test t) + { + return new Test(); + } + + public Clazz AddClazz(Clazz c) + { + return new Clazz(); + } } public sealed class Clazz @@ -62,88 +75,13 @@ namespace ProxyInterfaceConsumer public string Name { get; set; } } - public interface ITest + [ProxyInterfaceGenerator.Proxy(typeof(Test))] + public partial interface ITest { - int Id { get; set; } - - IClazz C { get; } - - IList Cs { get; set; } - - int Add(string s); } - public interface IClazz + [ProxyInterfaceGenerator.Proxy(typeof(Clazz))] + public partial interface IClazz { - string Name { get; set; } } - - public class TestProxy : ITest - { - private Test _instance; - - private IClazz _clazz; - - private readonly IMapper _mapper; - - public TestProxy(Test instance) - { - _instance = instance; - - // _clazz = new ClazzProxy(_instance.C); - - _mapper = new MapperConfiguration(cfg => - { - //cfg.CreateMap(); - //cfg.CreateMap(); - - cfg.CreateMap(); - cfg.CreateMap(); - }).CreateMapper(); - } - - public int Id - { - get => _instance.Id; - set => _instance.Id = value; - } - - public IClazz C => _clazz; - - public IList Cs2 - { - get - { - //return null; // TinyMapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); - return _mapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); - } - - set - { - _instance.Cs = _mapper.Map>(value); - //_instance.Cs = TinyMapper.Map>(value); - } - } - - public IList Cs - { - get => _mapper.Map>(_instance.Cs); - - set => _instance.Cs = _mapper.Map>(value); - } - - public int Add(string s) => _instance.Add(s); - } - - public class ClazzProxy : IClazz - { - private Clazz _instance; - - public ClazzProxy(Clazz instance) - { - _instance = instance; - } - - public string Name { get => _instance.Name; set => _instance.Name = value; } - } -} +} \ No newline at end of file diff --git a/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj b/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj index 43d1dba..0b862a7 100644 --- a/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj +++ b/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj @@ -8,9 +8,7 @@ - - diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs b/src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs index db1dfa7..2c7ba90 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs +++ b/src/ProxyInterfaceSourceGenerator/Extensions/NamedTypeSymbolExtensions.cs @@ -71,4 +71,11 @@ internal static class NamedTypeSymbolExtensions $"{namedTypeSymbol.Name}Proxy" : $"{namedTypeSymbol.Name}Proxy<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>"; } + + public static string ResolveFullProxyClassName(this INamedTypeSymbol namedTypeSymbol) + { + return !namedTypeSymbol.IsGenericType ? + $"{namedTypeSymbol}Proxy" : + $"{namedTypeSymbol}Proxy<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>"; + } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs b/src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs index b4a1046..9505f49 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs +++ b/src/ProxyInterfaceSourceGenerator/Extensions/PropertySymbolExtensions.cs @@ -1,6 +1,6 @@ using Microsoft.CodeAnalysis; using ProxyInterfaceSourceGenerator.Enums; -using ProxyInterfaceSourceGenerator.Model; +using ProxyInterfaceSourceGenerator.Models; namespace ProxyInterfaceSourceGenerator.Extensions; diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/StringExtensions.cs b/src/ProxyInterfaceSourceGenerator/Extensions/StringExtensions.cs new file mode 100644 index 0000000..a21b0aa --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/Extensions/StringExtensions.cs @@ -0,0 +1,29 @@ +namespace ProxyInterfaceSourceGenerator.Extensions; + +internal static class StringExtensions +{ + // See https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/ + public static string GetDeterministicHashCodeAsString(this string str) + { + unchecked + { + int hash1 = (5381 << 16) + 5381; + int hash2 = hash1; + + for (int i = 0; i < str.Length; i += 2) + { + hash1 = ((hash1 << 5) + hash1) ^ str[i]; + if (i == str.Length - 1) + { + break; + } + + hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; + } + + int result = hash1 + hash2 * 1566083941; + + return result.ToString().Replace('-', '_'); + } + } +} \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs b/src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs index 95761bb..e24bd58 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs +++ b/src/ProxyInterfaceSourceGenerator/Extensions/TypeSymbolExtensions.cs @@ -20,8 +20,9 @@ internal static class TypeSymbolExtensions return TypeEnum.Complex; } - public static bool IsString(this ITypeSymbol ts) - { - return ts.ToString() == "string" || ts.ToString() == "string?"; - } + public static bool IsString(this ITypeSymbol ts) => + ts.ToString() == "string" || ts.ToString() == "string?"; + + internal static bool IsClass(this ITypeSymbol ts) => + ts.IsReferenceType && ts.TypeKind == TypeKind.Class; } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs index 448cc94..30d2201 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs @@ -1,6 +1,7 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Model; +using ProxyInterfaceSourceGenerator.Models; namespace ProxyInterfaceSourceGenerator.FileGenerators; @@ -36,11 +37,11 @@ internal abstract class BaseGenerator { if (!Context.ReplacedTypes.ContainsKey(typeSymbolAsString)) { - Context.ReplacedTypes.Add(typeSymbolAsString, existing.InterfaceName); + Context.ReplacedTypes.Add(typeSymbolAsString, existing.FullInterfaceName); } isReplaced = true; - return existing.InterfaceName; + return existing.FullInterfaceName; } if (typeSymbol is INamedTypeSymbol namedTypedSymbol) @@ -56,10 +57,10 @@ internal abstract class BaseGenerator if (!Context.ReplacedTypes.ContainsKey(typeArgumentAsString)) { - Context.ReplacedTypes.Add(typeArgumentAsString, exist.InterfaceName); + Context.ReplacedTypes.Add(typeArgumentAsString, exist.FullInterfaceName); } - propertyTypeAsStringToBeModified = propertyTypeAsStringToBeModified.Replace(typeArgumentAsString, exist.InterfaceName); + propertyTypeAsStringToBeModified = propertyTypeAsStringToBeModified.Replace(typeArgumentAsString, exist.FullInterfaceName); } } @@ -69,27 +70,28 @@ internal abstract class BaseGenerator return typeSymbolAsString; } - protected ClassSymbol GetNamedTypeSymbolByFullName(string name, IEnumerable? usings = null) + protected bool TryGetNamedTypeSymbolByFullName(TypeKind kind, string name, IEnumerable usings, [NotNullWhen(true)] out ClassSymbol? classSymbol) { + classSymbol = default; + // The GetTypeByMetadataName method returns null if no type matches the full name or if 2 or more types (in different assemblies) match the full name. var symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name); - if (symbol is not null) + if (symbol is not null && symbol.TypeKind == kind) { - return new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); + classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); + return true; } - if (usings is not null) + foreach (var @using in usings) { - foreach (var @using in usings) + symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName($"{@using}.{name}"); + if (symbol is not null && symbol.TypeKind == kind) { - symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName($"{@using}.{name}"); - if (symbol is not null) - { - return new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); - } + classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList()); + return true; } } - throw new Exception($"The type '{name}' is not found."); + return false; } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/FileData.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/FileData.cs deleted file mode 100644 index b86ee44..0000000 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/FileData.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace ProxyInterfaceSourceGenerator.FileGenerators; - -internal record FileData(string FileName, string Text); \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs index 2a15607..237d747 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/IFileGenerator.cs @@ -1,3 +1,5 @@ +using ProxyInterfaceSourceGenerator.Models; + namespace ProxyInterfaceSourceGenerator.FileGenerators; internal interface IFileGenerator diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs index eb4b764..4126668 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/IFilesGenerator.cs @@ -1,3 +1,5 @@ +using ProxyInterfaceSourceGenerator.Models; + namespace ProxyInterfaceSourceGenerator.FileGenerators; internal interface IFilesGenerator diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs index 988ceee..0500330 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -1,9 +1,10 @@ +using System.Diagnostics.CodeAnalysis; using System.Text; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using ProxyInterfaceSourceGenerator.Enums; using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Model; -using ProxyInterfaceSourceGenerator.SyntaxReceiver; +using ProxyInterfaceSourceGenerator.Models; using ProxyInterfaceSourceGenerator.Utils; namespace ProxyInterfaceSourceGenerator.FileGenerators; @@ -19,22 +20,35 @@ internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator { foreach (var ci in Context.CandidateInterfaces) { - yield return GenerateFile(ci.Key, ci.Value); + if (TryGenerateFile(ci.Key, ci.Value, out var file)) + { + yield return file; + } } } - private FileData GenerateFile(InterfaceDeclarationSyntax ci, ProxyData pd) + private bool TryGenerateFile(InterfaceDeclarationSyntax ci, ProxyData pd, [NotNullWhen(true)] out FileData? fileData) { - var sourceInterfaceSymbol = GetNamedTypeSymbolByFullName(ci.Identifier.ToString(), pd.Usings); - var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName, pd.Usings); - var interfaceName = targetClassSymbol.Symbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName); + fileData = default; - var file = new FileData( + if (!TryGetNamedTypeSymbolByFullName(TypeKind.Interface, ci.Identifier.ToString(), pd.Usings, out var sourceInterfaceSymbol)) + { + return false; + } + + if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.TypeName, pd.Usings, out var targetClassSymbol)) + { + return false; + } + + var interfaceName = targetClassSymbol.Symbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.ShortInterfaceName); + + fileData = new FileData( $"{sourceInterfaceSymbol.Symbol.GetFileName()}.g.cs", CreatePartialInterfaceCode(pd.Namespace, targetClassSymbol, interfaceName, pd.ProxyBaseClasses) ); - return file; + return true; } private string CreatePartialInterfaceCode( diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs index 4accc18..4470820 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs @@ -1,3 +1,5 @@ +using ProxyInterfaceSourceGenerator.Models; + namespace ProxyInterfaceSourceGenerator.FileGenerators; internal class ProxyAttributeGenerator : IFileGenerator diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs index bc8e942..6e2bf9c 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -1,15 +1,14 @@ +using System.Diagnostics.CodeAnalysis; using System.Text; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using ProxyInterfaceSourceGenerator.Enums; using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.Model; -using ProxyInterfaceSourceGenerator.SyntaxReceiver; +using ProxyInterfaceSourceGenerator.Models; using ProxyInterfaceSourceGenerator.Utils; namespace ProxyInterfaceSourceGenerator.FileGenerators; -internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator +internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator { public ProxyClassesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable) { @@ -19,32 +18,72 @@ internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator { foreach (var ci in Context.CandidateInterfaces) { - yield return GenerateFile(ci.Value, Context.CandidateInterfaces); + if (TryGenerateFile(ci.Value, out var file)) + { + yield return file; + } } } - private FileData GenerateFile(ProxyData pd, IDictionary candidateInterfaces) + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1024:Compare symbols correctly", Justification = "")] + private bool TryGenerateFile(ProxyData pd, [NotNullWhen(true)] out FileData? fileData) { - var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName, pd.Usings); - var interfaceName = targetClassSymbol.Symbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName); + fileData = default; + + if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.TypeName, pd.Usings, out var targetClassSymbol)) + { + return false; + } + + var interfaceName = targetClassSymbol.Symbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.ShortInterfaceName); var className = targetClassSymbol.Symbol.ResolveProxyClassName(); var constructorName = $"{targetClassSymbol.Symbol.Name}Proxy"; - var file = new FileData( + var extendsProxyClasses = targetClassSymbol.BaseTypes + .Join( + Context.CandidateInterfaces.Values.Select(v => v.RawTypeName), + bt => bt.ToString(), + ci => ci, (bt, _) => bt + ).ToList(); + + fileData = new FileData( $"{targetClassSymbol.Symbol.GetFileName()}Proxy.g.cs", - CreateProxyClassCode(pd.Namespace, targetClassSymbol, pd.ProxyBaseClasses, interfaceName, className, constructorName) + CreateProxyClassCode(pd, targetClassSymbol, extendsProxyClasses, interfaceName, className, constructorName) ); - return file; + return true; } private string CreateProxyClassCode( - string ns, + ProxyData pd, ClassSymbol targetClassSymbol, - bool proxyBaseClasses, + List extendsProxyClasses, string interfaceName, string className, - string constructorName) => $@"//---------------------------------------------------------------------------------------- + string constructorName) + { + var extendsFullNames = extendsProxyClasses.Select(e => e.ResolveFullProxyClassName()).ToList(); + var extends = extendsProxyClasses.Any() ? $"{string.Join(", ", extendsFullNames)}, " : string.Empty; + var @base = extendsProxyClasses.Any() ? " : base(instance)" : string.Empty; + var @new = extendsProxyClasses.Any() ? "new " : string.Empty; + var instanceBaseDefinition = extendsProxyClasses.Any() ? $"public {extendsProxyClasses.First()} _InstanceBase {{ get; }}\r\n" : string.Empty; + var instanceBaseSet = extendsProxyClasses.Any() ? "_InstanceBase = instance;" : string.Empty; + + var properties = GeneratePublicProperties(targetClassSymbol, pd.ProxyBaseClasses); + var methods = GeneratePublicMethods(targetClassSymbol, pd.ProxyBaseClasses); + var events = GenerateEvents(targetClassSymbol, pd.ProxyBaseClasses); + + var configurationForAutoMapper = string.Empty; + var privateAutoMapper = string.Empty; + var usingAutoMapper = string.Empty; + if (Context.ReplacedTypes.Any()) + { + configurationForAutoMapper = GenerateMapperConfigurationForAutoMapper(); + privateAutoMapper = GeneratePrivateAutoMapper(); + usingAutoMapper = "using AutoMapper;"; + } + + return $@"//---------------------------------------------------------------------------------------- // // This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator. // @@ -55,52 +94,52 @@ internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator {(SupportsNullable ? "#nullable enable" : string.Empty)} using System; -using AutoMapper; +{usingAutoMapper} -namespace {ns} +namespace {pd.Namespace} {{ - public partial class {className} : {interfaceName} + public partial class {className} : {extends}{interfaceName} {{ - public {targetClassSymbol.Symbol} _Instance {{ get; }} + public {@new}{targetClassSymbol.Symbol} _Instance {{ get; }} + {instanceBaseDefinition} -{GeneratePublicProperties(targetClassSymbol, proxyBaseClasses)} +{properties} -{GeneratePublicMethods(targetClassSymbol, proxyBaseClasses)} +{methods} -{GenerateEvents(targetClassSymbol, proxyBaseClasses)} +{events} - public {constructorName}({targetClassSymbol} instance) + public {constructorName}({targetClassSymbol} instance){@base} {{ _Instance = instance; + {instanceBaseSet} -{GenerateMapperConfigurationForAutoMapper()} +{configurationForAutoMapper} }} -{GeneratePrivateAutoMapper()} +{privateAutoMapper} }} }} {(SupportsNullable ? "#nullable disable" : string.Empty)}"; + } - private string GeneratePrivateAutoMapper() + private static string GeneratePrivateAutoMapper() { - return Context.ReplacedTypes.Count == 0 ? string.Empty : " private readonly IMapper _mapper;"; + return " private readonly IMapper _mapper;"; } private string GenerateMapperConfigurationForAutoMapper() { - if (Context.ReplacedTypes.Count == 0) - { - return string.Empty; - } - var str = new StringBuilder(); str.AppendLine(" _mapper = new MapperConfiguration(cfg =>"); str.AppendLine(" {"); foreach (var replacedType in Context.ReplacedTypes) { - str.AppendLine($" cfg.CreateMap<{replacedType.Key}, {replacedType.Value}>();"); - str.AppendLine($" cfg.CreateMap<{replacedType.Value}, {replacedType.Key}>();"); + var proxy = $"{replacedType.Key}Proxy"; + + str.AppendLine($" cfg.CreateMap<{replacedType.Key}, {replacedType.Value}>().ConstructUsing(instance => new {proxy}(instance));"); + str.AppendLine($" cfg.CreateMap<{replacedType.Value}, {replacedType.Key}>().ConstructUsing(proxy => (({proxy}) proxy)._Instance);"); } str.AppendLine(" }).CreateMapper();"); @@ -167,11 +206,8 @@ namespace {ns} str.AppendLine($" {ps.Type} {ps.GetSanitizedName()}_{normalOrMap};"); } -#pragma warning disable RS1024 // Compare symbols correctly - int hash = method.ReturnType.GetHashCode(); -#pragma warning restore RS1024 // Compare symbols correctly - - var alternateReturnVariableName = $"result_{Math.Abs(hash)}"; + var methodName = method.GetMethodNameWithOptionalTypeParameters(); + var alternateReturnVariableName = $"result_{methodName.GetDeterministicHashCodeAsString()}"; string instance = !method.IsStatic ? "_Instance" : @@ -179,11 +215,11 @@ namespace {ns} if (returnTypeAsString == "void") { - str.AppendLine($" {instance}.{method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", invokeParameters)});"); + str.AppendLine($" {instance}.{methodName}({string.Join(", ", invokeParameters)});"); } else { - str.AppendLine($" var {alternateReturnVariableName} = {instance}.{method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", invokeParameters)});"); + str.AppendLine($" var {alternateReturnVariableName} = {instance}.{methodName}({string.Join(", ", invokeParameters)});"); } foreach (var ps in method.Parameters.Where(p => p.RefKind == RefKind.Out)) diff --git a/src/ProxyInterfaceSourceGenerator/Model/ClassSymbol.cs b/src/ProxyInterfaceSourceGenerator/Models/ClassSymbol.cs similarity index 83% rename from src/ProxyInterfaceSourceGenerator/Model/ClassSymbol.cs rename to src/ProxyInterfaceSourceGenerator/Models/ClassSymbol.cs index 4a418f0..4c422ae 100644 --- a/src/ProxyInterfaceSourceGenerator/Model/ClassSymbol.cs +++ b/src/ProxyInterfaceSourceGenerator/Models/ClassSymbol.cs @@ -1,6 +1,6 @@ using Microsoft.CodeAnalysis; -namespace ProxyInterfaceSourceGenerator.Model; +namespace ProxyInterfaceSourceGenerator.Models; internal record ClassSymbol(INamedTypeSymbol Symbol, List BaseTypes, List Interfaces) { diff --git a/src/ProxyInterfaceSourceGenerator/Context.cs b/src/ProxyInterfaceSourceGenerator/Models/Context.cs similarity index 81% rename from src/ProxyInterfaceSourceGenerator/Context.cs rename to src/ProxyInterfaceSourceGenerator/Models/Context.cs index e065eac..ae7b059 100644 --- a/src/ProxyInterfaceSourceGenerator/Context.cs +++ b/src/ProxyInterfaceSourceGenerator/Models/Context.cs @@ -1,16 +1,15 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using ProxyInterfaceSourceGenerator.SyntaxReceiver; - -namespace ProxyInterfaceSourceGenerator; - -internal record Context -{ - public GeneratorExecutionContext GeneratorExecutionContext { get; init; } - - // public List GeneratedData { get; } = new List(); - - public IDictionary CandidateInterfaces { get; init; } = default!; - - public Dictionary ReplacedTypes { get; } = new Dictionary(); +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ProxyInterfaceSourceGenerator.Models; + +internal record Context +{ + public GeneratorExecutionContext GeneratorExecutionContext { get; init; } + + // public List GeneratedData { get; } = new List(); + + public IDictionary CandidateInterfaces { get; init; } = default!; + + public Dictionary ReplacedTypes { get; } = new Dictionary(); } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ContextData.cs b/src/ProxyInterfaceSourceGenerator/Models/ContextData.cs similarity index 63% rename from src/ProxyInterfaceSourceGenerator/ContextData.cs rename to src/ProxyInterfaceSourceGenerator/Models/ContextData.cs index 85fc823..7910286 100644 --- a/src/ProxyInterfaceSourceGenerator/ContextData.cs +++ b/src/ProxyInterfaceSourceGenerator/Models/ContextData.cs @@ -1,12 +1,10 @@ -using ProxyInterfaceSourceGenerator.FileGenerators; - -namespace ProxyInterfaceSourceGenerator; - -internal record ContextData -{ - public string? InterfaceName { get; init; } - - public string? ClassName { get; init; } - - public FileData FileData { get; init; } = default!; +namespace ProxyInterfaceSourceGenerator.Models; + +internal record ContextData +{ + public string? InterfaceName { get; init; } + + public string? ClassName { get; init; } + + public FileData FileData { get; init; } = default!; } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Models/FileData.cs b/src/ProxyInterfaceSourceGenerator/Models/FileData.cs new file mode 100644 index 0000000..78598ea --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/Models/FileData.cs @@ -0,0 +1,3 @@ +namespace ProxyInterfaceSourceGenerator.Models; + +internal record FileData(string FileName, string Text); \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs b/src/ProxyInterfaceSourceGenerator/Models/ProxyData.cs similarity index 57% rename from src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs rename to src/ProxyInterfaceSourceGenerator/Models/ProxyData.cs index bd7a443..510b158 100644 --- a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs +++ b/src/ProxyInterfaceSourceGenerator/Models/ProxyData.cs @@ -1,11 +1,12 @@ -namespace ProxyInterfaceSourceGenerator.SyntaxReceiver; - -internal record ProxyData -( - string Namespace, - string InterfaceName, - string RawTypeName, - string TypeName, - List Usings, - bool ProxyBaseClasses +namespace ProxyInterfaceSourceGenerator.Models; + +internal record ProxyData +( + string Namespace, + string ShortInterfaceName, + string FullInterfaceName, + string RawTypeName, + string TypeName, + List Usings, + bool ProxyBaseClasses ); \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs index e2436e2..5b148bc 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using ProxyInterfaceSourceGenerator.FileGenerators; +using ProxyInterfaceSourceGenerator.Models; using ProxyInterfaceSourceGenerator.SyntaxReceiver; namespace ProxyInterfaceSourceGenerator; @@ -10,7 +11,7 @@ namespace ProxyInterfaceSourceGenerator; [Generator] internal class ProxyInterfaceCodeGenerator : ISourceGenerator { - private readonly ProxyAttributeGenerator _proxyAttributeGenerator = new ProxyAttributeGenerator(); + private readonly ProxyAttributeGenerator _proxyAttributeGenerator = new (); public void Initialize(GeneratorInitializationContext context) { @@ -24,21 +25,21 @@ internal class ProxyInterfaceCodeGenerator : ISourceGenerator public void Execute(GeneratorExecutionContext context) { - if (context.ParseOptions is not CSharpParseOptions csharpParseOptions) - { - throw new NotSupportedException("Only C# is supported."); - } - - if (context.SyntaxReceiver is not ProxySyntaxReceiver receiver) - { - return; - } - - // https://github.com/reactiveui/refit/blob/main/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs - var supportsNullable = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp8; - try { + if (context.ParseOptions is not CSharpParseOptions csharpParseOptions) + { + throw new NotSupportedException("Only C# is supported."); + } + + if (context.SyntaxReceiver is not ProxySyntaxReceiver receiver) + { + throw new NotSupportedException($"Only {nameof(ProxySyntaxReceiver)} is supported."); + } + + // https://github.com/reactiveui/refit/blob/main/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs + var supportsNullable = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp8; + GenerateProxyAttribute(context, receiver); GeneratePartialInterfaces(context, receiver, supportsNullable); GenerateProxyClasses(context, receiver, supportsNullable); @@ -51,7 +52,7 @@ internal class ProxyInterfaceCodeGenerator : ISourceGenerator private void GenerateError(GeneratorExecutionContext context, Exception exception) { - var message = $"/*\r\n{nameof(ProxyInterfaceCodeGenerator)}\r\n\r\n{exception}\r\n\r\n{exception.StackTrace}*/"; + var message = $"/*\r\n{nameof(ProxyInterfaceCodeGenerator)}\r\n\r\n[Exception]\r\n{exception}\r\n\r\n[StackTrace]\r\n{exception.StackTrace}*/"; context.AddSource("Error.g", SourceText.From(message, Encoding.UTF8)); } @@ -76,9 +77,9 @@ internal class ProxyInterfaceCodeGenerator : ISourceGenerator }; var partialInterfacesGenerator = new PartialInterfacesGenerator(context, supportsNullable); - foreach (var data in partialInterfacesGenerator.GenerateFiles()) + foreach (var (fileName, text) in partialInterfacesGenerator.GenerateFiles()) { - context.GeneratorExecutionContext.AddSource(data.FileName, SourceText.From(data.Text, Encoding.UTF8)); + context.GeneratorExecutionContext.AddSource(fileName, SourceText.From(text, Encoding.UTF8)); } } @@ -91,9 +92,9 @@ internal class ProxyInterfaceCodeGenerator : ISourceGenerator }; var proxyClassesGenerator = new ProxyClassesGenerator(context, supportsNullable); - foreach (var data in proxyClassesGenerator.GenerateFiles()) + foreach (var (fileName, text) in proxyClassesGenerator.GenerateFiles()) { - context.GeneratorExecutionContext.AddSource(data.FileName, SourceText.From(data.Text, Encoding.UTF8)); + context.GeneratorExecutionContext.AddSource(fileName, SourceText.From(text, Encoding.UTF8)); } } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj index 9ae2859..7a43460 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj @@ -36,7 +36,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -46,11 +46,19 @@ - - + + + + ProxyClassesGenerator.cs + + + + + + diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs index 3adf2d2..ffc120e 100644 --- a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs +++ b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using ProxyInterfaceSourceGenerator.Extensions; +using ProxyInterfaceSourceGenerator.Models; namespace ProxyInterfaceSourceGenerator.SyntaxReceiver; @@ -44,13 +45,13 @@ internal class ProxySyntaxReceiver : ISyntaxReceiver var usings = new List(); string ns = string.Empty; - if (SyntaxNodeUtils.TryGetParentSyntax(interfaceDeclarationSyntax, out NamespaceDeclarationSyntax? namespaceDeclarationSyntax)) + if (interfaceDeclarationSyntax.TryGetParentSyntax(out NamespaceDeclarationSyntax? namespaceDeclarationSyntax)) { ns = namespaceDeclarationSyntax.Name.ToString(); usings.Add(ns); } - if (SyntaxNodeUtils.TryGetParentSyntax(interfaceDeclarationSyntax, out CompilationUnitSyntax? cc)) + if (interfaceDeclarationSyntax.TryGetParentSyntax(out CompilationUnitSyntax? cc)) { foreach (var @using in cc.Usings) { @@ -58,7 +59,9 @@ internal class ProxySyntaxReceiver : ISyntaxReceiver } } - string rawTypeName = ((TypeOfExpressionSyntax)argumentList.Arguments[0].Expression).Type.ToString(); + var type = ((TypeOfExpressionSyntax)argumentList.Arguments[0].Expression).Type; + + string rawTypeName = type.ToString(); bool proxyAllClasses; try { @@ -73,6 +76,7 @@ internal class ProxySyntaxReceiver : ISyntaxReceiver ( ns, interfaceDeclarationSyntax.Identifier.ToString(), + $"{ns}.{interfaceDeclarationSyntax.Identifier}", rawTypeName, ConvertTypeName(rawTypeName), usings, diff --git a/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs b/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs index 486b2f6..d5467f8 100644 --- a/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs +++ b/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs @@ -1,5 +1,5 @@ using Microsoft.CodeAnalysis; -using ProxyInterfaceSourceGenerator.Model; +using ProxyInterfaceSourceGenerator.Models; namespace ProxyInterfaceSourceGenerator.Utils; diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.HumanProxy.g.cs b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.HumanProxy.g.cs index 3beaf96..f0eae81 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.HumanProxy.g.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.HumanProxy.g.cs @@ -9,16 +9,19 @@ #nullable enable using System; -using AutoMapper; + namespace ProxyInterfaceSourceGeneratorTests.Source { - public class HumanProxy : IHuman + public partial class HumanProxy : IHuman { public ProxyInterfaceSourceGeneratorTests.Source.Human _Instance { get; } + public bool IsAlive { get => _Instance.IsAlive; set => _Instance.IsAlive = value; } + public string GetterOnly { get => _Instance.GetterOnly; } + @@ -28,6 +31,7 @@ namespace ProxyInterfaceSourceGeneratorTests.Source public HumanProxy(ProxyInterfaceSourceGeneratorTests.Source.Human instance) { _Instance = instance; + } diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IHuman.g.cs b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IHuman.g.cs index 5c712c3..f3026a9 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IHuman.g.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IHuman.g.cs @@ -16,6 +16,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source { bool IsAlive { get; set; } + string GetterOnly { get; } + diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPerson.g.cs b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPerson.g.cs index bf16cf5..6499f7b 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPerson.g.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPerson.g.cs @@ -24,6 +24,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source + System.Collections.Generic.IList AddHuman(ProxyInterfaceSourceGeneratorTests.Source.IHuman h); + void Void(); string HelloWorld(string name); diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPersonExtends.g.cs b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPersonExtends.g.cs index d4b35e3..2a67d8a 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPersonExtends.g.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.IPersonExtends.g.cs @@ -26,6 +26,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source bool IsAlive { get; set; } + string GetterOnly { get; } + string StaticMethod(int x, string y); diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonExtendsProxy.g.cs b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonExtendsProxy.g.cs index 2a5d10d..53278f3 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonExtendsProxy.g.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonExtendsProxy.g.cs @@ -9,13 +9,14 @@ #nullable enable using System; -using AutoMapper; + namespace ProxyInterfaceSourceGeneratorTests.Source { public partial class PersonExtendsProxy : IPersonExtends { public ProxyInterfaceSourceGeneratorTests.Source.PersonExtends _Instance { get; } + public string StaticString { get => ProxyInterfaceSourceGeneratorTests.Source.PersonExtends.StaticString; set => ProxyInterfaceSourceGeneratorTests.Source.PersonExtends.StaticString = value; } @@ -29,14 +30,16 @@ namespace ProxyInterfaceSourceGeneratorTests.Source public bool IsAlive { get => _Instance.IsAlive; set => _Instance.IsAlive = value; } + public string GetterOnly { get => _Instance.GetterOnly; } + public string StaticMethod(int x, string y) { int x_ = x; string y_ = y; - var result_6851397 = ProxyInterfaceSourceGeneratorTests.Source.PersonExtends.StaticMethod(x_, y_); - return result_6851397; + var result__1647028461 = ProxyInterfaceSourceGeneratorTests.Source.PersonExtends.StaticMethod(x_, y_); + return result__1647028461; } public void Void() @@ -47,8 +50,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source public string HelloWorld(string name) { string name_ = name; - var result_6851397 = _Instance.HelloWorld(name_); - return result_6851397; + var result_282270798 = _Instance.HelloWorld(name_); + return result_282270798; } public void WithParams(params string[] values) @@ -61,15 +64,15 @@ namespace ProxyInterfaceSourceGeneratorTests.Source { string s_ = s; string @string_ = @string; - var result_6851397 = _Instance.Add(s_, @string_); - return result_6851397; + var result__1127157211 = _Instance.Add(s_, @string_); + return result__1127157211; } public int DefaultValue(int x = 100) { int x_ = x; - var result_3873514 = _Instance.DefaultValue(x_); - return result_3873514; + var result__378509684 = _Instance.DefaultValue(x_); + return result__378509684; } public void In_Out_Ref1(in int a, out int b, ref int c) @@ -86,26 +89,26 @@ namespace ProxyInterfaceSourceGeneratorTests.Source int x_ = x; T1 t1_ = t1; T2 t2_ = t2; - var result_14331071 = _Instance.Generic2(x_, t1_, t2_); - return result_14331071; + var result_542538942 = _Instance.Generic2(x_, t1_, t2_); + return result_542538942; } public System.Threading.Tasks.Task Method1Async() { - var result_39535275 = _Instance.Method1Async(); - return result_39535275; + var result__57678382 = _Instance.Method1Async(); + return result__57678382; } public System.Threading.Tasks.Task Method2Async() { - var result_772784336 = _Instance.Method2Async(); - return result_772784336; + var result__57677169 = _Instance.Method2Async(); + return result__57677169; } public System.Threading.Tasks.Task Method3Async() { - var result_769806453 = _Instance.Method3Async(); - return result_769806453; + var result__57684656 = _Instance.Method3Async(); + return result__57684656; } @@ -115,6 +118,7 @@ namespace ProxyInterfaceSourceGeneratorTests.Source public PersonExtendsProxy(ProxyInterfaceSourceGeneratorTests.Source.PersonExtends instance) { _Instance = instance; + } diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonProxy.g.cs b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonProxy.g.cs index 9e09b36..0b8ff64 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonProxy.g.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Destination/ProxyInterfaceSourceGeneratorTests.Source.PersonProxy.g.cs @@ -13,9 +13,11 @@ using AutoMapper; namespace ProxyInterfaceSourceGeneratorTests.Source { - public class PersonProxy : IPerson + public partial class PersonProxy : ProxyInterfaceSourceGeneratorTests.Source.HumanProxy, IPerson { - public ProxyInterfaceSourceGeneratorTests.Source.Person _Instance { get; } + public new ProxyInterfaceSourceGeneratorTests.Source.Person _Instance { get; } + public ProxyInterfaceSourceGeneratorTests.Source.Human _InstanceBase { get; } + public string Name { get => _Instance.Name; set => _Instance.Name = value; } @@ -27,6 +29,13 @@ namespace ProxyInterfaceSourceGeneratorTests.Source + public System.Collections.Generic.IList AddHuman(ProxyInterfaceSourceGeneratorTests.Source.IHuman h) + { + ProxyInterfaceSourceGeneratorTests.Source.Human h_ = _mapper.Map(h); + var result_907493286 = _Instance.AddHuman(h_); + return _mapper.Map>(result_907493286); + } + public void Void() { _Instance.Void(); @@ -35,8 +44,8 @@ namespace ProxyInterfaceSourceGeneratorTests.Source public string HelloWorld(string name) { string name_ = name; - var result_37309470 = _Instance.HelloWorld(name_); - return result_37309470; + var result_282270798 = _Instance.HelloWorld(name_); + return result_282270798; } public void WithParams(params string[] values) @@ -49,15 +58,15 @@ namespace ProxyInterfaceSourceGeneratorTests.Source { string s_ = s; string @string_ = @string; - var result_37309470 = _Instance.Add(s_, @string_); - return result_37309470; + var result__1127157211 = _Instance.Add(s_, @string_); + return result__1127157211; } public int DefaultValue(int x = 100) { int x_ = x; - var result_24216618 = _Instance.DefaultValue(x_); - return result_24216618; + var result__378509684 = _Instance.DefaultValue(x_); + return result__378509684; } public void In_Out_Ref1(in int a, out int b, ref int c) @@ -74,41 +83,46 @@ namespace ProxyInterfaceSourceGeneratorTests.Source int x_ = x; T1 t1_ = t1; T2 t2_ = t2; - var result_60333940 = _Instance.Generic2(x_, t1_, t2_); - return result_60333940; + var result_542538942 = _Instance.Generic2(x_, t1_, t2_); + return result_542538942; } public System.Threading.Tasks.Task Method1Async() { - var result_2292327 = _Instance.Method1Async(); - return result_2292327; + var result__57678382 = _Instance.Method1Async(); + return result__57678382; } public System.Threading.Tasks.Task Method2Async() { - var result_1229624901 = _Instance.Method2Async(); - return result_1229624901; + var result__57677169 = _Instance.Method2Async(); + return result__57677169; } public System.Threading.Tasks.Task Method3Async() { - var result_1242717753 = _Instance.Method3Async(); - return result_1242717753; + var result__57684656 = _Instance.Method3Async(); + return result__57684656; } - public PersonProxy(ProxyInterfaceSourceGeneratorTests.Source.Person instance) + public PersonProxy(ProxyInterfaceSourceGeneratorTests.Source.Person instance) : base(instance) { _Instance = instance; + _InstanceBase = instance; + _mapper = new MapperConfiguration(cfg => + { + cfg.CreateMap().ConstructUsing(instance => new ProxyInterfaceSourceGeneratorTests.Source.HumanProxy(instance)); + cfg.CreateMap().ConstructUsing(proxy => ((ProxyInterfaceSourceGeneratorTests.Source.HumanProxy) proxy)._Instance); + }).CreateMapper(); } - - public bool IsAlive { get; set; } + private readonly IMapper _mapper; } } #nullable disable \ No newline at end of file diff --git a/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTest.cs b/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTest.cs index 9a4e200..75a7d78 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTest.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTest.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; using System.IO; -using AnyOfTypes; using CSharp.SourceGenerators.Extensions; using CSharp.SourceGenerators.Extensions.Models; using FluentAssertions; using ProxyInterfaceSourceGenerator; +using ProxyInterfaceSourceGeneratorTests.Source; using Xunit; -using Xunit.Sdk; namespace ProxyInterfaceSourceGeneratorTests { @@ -20,6 +17,36 @@ namespace ProxyInterfaceSourceGeneratorTests public ProxyInterfaceSourceGeneratorTest() { _sut = new ProxyInterfaceCodeGenerator(); + + var pp = new PersonProxy(new Person()); + + var h = pp.AddHuman(new HumanProxy(new Human())); + + int x = 0; + } + + [Fact] + public void GenerateFiles_ForStruct_Should_Not_GenerateProxyCode() + { + // Arrange + var path = "./Source/IMyStruct.cs"; + var sourceFile = new SourceFile + { + Path = path, + Text = File.ReadAllText(path), + AttributeToAddToInterface = new ExtraAttribute + { + Name = "ProxyInterfaceGenerator.Proxy", + ArgumentList = "typeof(ProxyInterfaceSourceGeneratorTests.Source.MyStruct)" + } + }; + + // Act + var result = _sut.Execute(new[] { sourceFile }); + + // Assert + result.Valid.Should().BeTrue(); + result.Files.Should().HaveCount(1); } [Fact] @@ -111,7 +138,7 @@ namespace ProxyInterfaceSourceGeneratorTests result.Valid.Should().BeTrue(); result.Files.Should().HaveCount(5); - throw new Exception(); + // throw new Exception(); // Assert attribute var attribute = result.Files[0].SyntaxTree; diff --git a/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj b/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj index aa629d4..fdf3227 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj +++ b/tests/ProxyInterfaceSourceGeneratorTests/ProxyInterfaceSourceGeneratorTests.csproj @@ -9,6 +9,7 @@ + @@ -28,26 +29,10 @@ - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - + PreserveNewest diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/Human.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/Human.cs index 7adb348..7f36057 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/Human.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/Human.cs @@ -1,7 +1,11 @@ +using Xunit.Sdk; + namespace ProxyInterfaceSourceGeneratorTests.Source { public class Human { public bool IsAlive { get; set; } + + public string GetterOnly => "x"; } } \ No newline at end of file diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/IMyStruct.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/IMyStruct.cs new file mode 100644 index 0000000..ed8a7b9 --- /dev/null +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/IMyStruct.cs @@ -0,0 +1,6 @@ +namespace ProxyInterfaceSourceGeneratorTests.Source +{ + public partial interface IMyStruct + { + } +} \ No newline at end of file diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/MyStruct.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/MyStruct.cs new file mode 100644 index 0000000..e2b579e --- /dev/null +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/MyStruct.cs @@ -0,0 +1,7 @@ +namespace ProxyInterfaceSourceGeneratorTests.Source +{ + public struct MyStruct + { + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs index 66d430d..b18defe 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/Person.cs @@ -1,9 +1,15 @@ +using System.Collections.Generic; using System.Threading.Tasks; namespace ProxyInterfaceSourceGeneratorTests.Source { - public class Person + public class Person : Human { + public IList AddHuman(Human h) + { + return new List { h, new Human { IsAlive = true } }; + } + public string Name { get; set; } public string? StringNullable { get; set; } diff --git a/tests/ProxyInterfaceSourceGeneratorTests/Source/PersonExtends.cs b/tests/ProxyInterfaceSourceGeneratorTests/Source/PersonExtends.cs index 4ded778..6ff3a06 100644 --- a/tests/ProxyInterfaceSourceGeneratorTests/Source/PersonExtends.cs +++ b/tests/ProxyInterfaceSourceGeneratorTests/Source/PersonExtends.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; namespace ProxyInterfaceSourceGeneratorTests.Source