diff --git a/src-examples/ProxyInterfaceConsumer/IPerson.cs b/src-examples/ProxyInterfaceConsumer/IPerson.cs index 08336e2..4c75259 100644 --- a/src-examples/ProxyInterfaceConsumer/IPerson.cs +++ b/src-examples/ProxyInterfaceConsumer/IPerson.cs @@ -1,6 +1,6 @@ namespace SourceGeneratorInterface { - [ProxyInterfaceGenerator.Proxy(typeof(SourceGeneratorInterface.Person))] + [ProxyInterfaceGenerator.Proxy(typeof(SourceGeneratorInterface.Person), true)] public partial interface IPerson { } diff --git a/src/ProxyInterfaceSourceGenerator/Compatibility/IsExternalInit.cs b/src/ProxyInterfaceSourceGenerator/Compatibility/IsExternalInit.cs new file mode 100644 index 0000000..83c76ea --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/Compatibility/IsExternalInit.cs @@ -0,0 +1,7 @@ +// https://bartwullems.blogspot.com/2021/01/c-9use-record-types-in-net-standard-20.html +#if NETSTANDARD2_0 +namespace System.Runtime.CompilerServices +{ + public class IsExternalInit { } +} +#endif \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/Data.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/Data.cs index 19f522c..e6161f4 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/Data.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/Data.cs @@ -2,8 +2,8 @@ { internal record Data { - public string FileName { get; set; } + public string FileName { get; init; } - public string Text { get; set; } + public string Text { get; init; } } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs index 873282e..0b8fd44 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -1,20 +1,21 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.FileGenerators; +using ProxyInterfaceSourceGenerator.SyntaxReceiver; using ProxyInterfaceSourceGenerator.Utils; -namespace ClassLibrarySourceGen +namespace ProxyInterfaceSourceGenerator.FileGenerators { internal class PartialInterfacesGenerator : IFilesGenerator { private readonly GeneratorExecutionContext _context; - private readonly IDictionary _candidateInterfaces; + private readonly IDictionary _candidateInterfaces; - public PartialInterfacesGenerator(GeneratorExecutionContext context, IDictionary candidateInterfaces) + public PartialInterfacesGenerator(GeneratorExecutionContext context, IDictionary candidateInterfaces) { _context = context; _candidateInterfaces = candidateInterfaces; @@ -24,11 +25,18 @@ namespace ClassLibrarySourceGen { foreach (var ci in _candidateInterfaces) { - string interfaceName = $"I{ci.Value.Split('.').Last()}"; + var symbol = _context.Compilation.GetTypeByMetadataName(ci.Value.TypeName); + if (symbol is null) + { + throw new Exception($"The type '{ci.Value.TypeName}' is not found."); + } + + string interfaceName = $"I{ci.Value.TypeName.Split('.').Last()}"; + yield return new Data { FileName = $"I{interfaceName}.cs", - Text = CreatePartialInterfaceCode(_context.Compilation.GetTypeByMetadataName(ci.Value), interfaceName) + Text = CreatePartialInterfaceCode(symbol, interfaceName) }; } } diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs index 763fc6b..bd65dc0 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyAttributeGenerator.cs @@ -17,10 +17,12 @@ namespace ProxyInterfaceGenerator public class {ClassName} : Attribute {{ public Type Type {{ get; }} + public bool ProxyAll {{ get; }} - public {ClassName}(Type type) + public {ClassName}(Type type, bool proxyAll = true) {{ Type = type; + ProxyAll = proxyAll; }} }} }}" diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs index bb1d4bb..0433f05 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -1,20 +1,21 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using ProxyInterfaceSourceGenerator.Extensions; -using ProxyInterfaceSourceGenerator.FileGenerators; +using ProxyInterfaceSourceGenerator.SyntaxReceiver; using ProxyInterfaceSourceGenerator.Utils; -namespace ClassLibrarySourceGen +namespace ProxyInterfaceSourceGenerator.FileGenerators { internal class ProxyClassesGenerator : IFilesGenerator { private readonly GeneratorExecutionContext _context; - private readonly IDictionary _candidateInterfaces; + private readonly IDictionary _candidateInterfaces; - public ProxyClassesGenerator(GeneratorExecutionContext context, IDictionary candidateInterfaces) + public ProxyClassesGenerator(GeneratorExecutionContext context, IDictionary candidateInterfaces) { _context = context; _candidateInterfaces = candidateInterfaces; @@ -24,24 +25,31 @@ namespace ClassLibrarySourceGen { foreach (var ci in _candidateInterfaces) { - string interfaceName = $"{ci.Value.Split('.').Last()}"; + var symbol = _context.Compilation.GetTypeByMetadataName(ci.Value.TypeName); + if (symbol is null) + { + throw new Exception($"The type '{ci.Value.TypeName}' is not found."); + } + + string className = $"{ci.Value.TypeName.Split('.').Last()}"; + yield return new Data { - FileName = $"{interfaceName}Proxy.cs", - Text = CreateProxyClassCode(_context.Compilation.GetTypeByMetadataName(ci.Value), interfaceName) + FileName = $"{className}Proxy.cs", + Text = CreateProxyClassCode(symbol, className) }; } } - private string CreateProxyClassCode(INamedTypeSymbol symbol, string interfaceName) => $@"using System; + private string CreateProxyClassCode(INamedTypeSymbol symbol, string className) => $@"using System; namespace {symbol.ContainingNamespace} {{ - public class {interfaceName}Proxy : I{interfaceName} + public class {className}Proxy : I{className} {{ - private {interfaceName} _instance; + private {className} _instance; - public {interfaceName}Proxy({interfaceName} instance) + public {className}Proxy({className} instance) {{ _instance = instance; }} diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs index f0619a6..e1e1f19 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs @@ -1,10 +1,8 @@ -using System; -using System.Diagnostics; -using System.Text; -using ClassLibrarySourceGen; +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using ProxyInterfaceSourceGenerator.FileGenerators; +using ProxyInterfaceSourceGenerator.SyntaxReceiver; namespace ProxyInterfaceSourceGenerator { @@ -15,9 +13,9 @@ namespace ProxyInterfaceSourceGenerator public void Initialize(GeneratorInitializationContext context) { - //if (!Debugger.IsAttached) + //if (!System.DiagnosticsDebugger.IsAttached) //{ - // Debugger.Launch(); + // System.DiagnosticsDebugger.Launch(); //} context.RegisterForSyntaxNotifications(() => new ProxySyntaxReceiver()); @@ -32,9 +30,6 @@ namespace ProxyInterfaceSourceGenerator return; } - var p = context.Compilation.GetTypeByMetadataName("SourceGeneratorInterface.Person"); - var ec = context.Compilation.GetTypeByMetadataName("Microsoft.CodeAnalysis.GeneratorExecutionContext"); - var partialInterfacesGenerator = new PartialInterfacesGenerator(context, receiver.CandidateInterfaces); foreach (var data in partialInterfacesGenerator.GenerateFiles()) { @@ -115,26 +110,4 @@ namespace ProxyInterfaceSourceGenerator public string Name { get => _instance.Name; set => _instance.Name = value; } } - - public interface IPrintable - { - int Length { get; } - int Count { get; } - void Print1(); - void Print2(); - } - - public class PrinterV1 : IPrintable - { - public int Length => 100; - public int Count => 200; - public void Print1() - { - Console.WriteLine("PrinterV1 - Print1"); - } - public void Print2() - { - Console.WriteLine("PrinterV1 - Print2"); - } - } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj index 39620e9..5f89e13 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj @@ -3,6 +3,7 @@ netstandard2.0 9 + enable diff --git a/src/ProxyInterfaceSourceGenerator/ProxySyntaxReceiver.cs b/src/ProxyInterfaceSourceGenerator/ProxySyntaxReceiver.cs deleted file mode 100644 index 44de8e3..0000000 --- a/src/ProxyInterfaceSourceGenerator/ProxySyntaxReceiver.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace ProxyInterfaceSourceGenerator -{ - internal class ProxySyntaxReceiver : ISyntaxReceiver - { - public IDictionary CandidateInterfaces { get; } = new Dictionary(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is InterfaceDeclarationSyntax interfaceDeclarationSyntax && TryGet(interfaceDeclarationSyntax, out string typeName)) - { - CandidateInterfaces.Add(interfaceDeclarationSyntax, typeName); - } - } - - private static bool TryGet(InterfaceDeclarationSyntax interfaceDeclarationSyntax, out string typeName) - { - typeName = null; - - // TODO : how to check if the InterfaceDeclarationSyntax has 'partial' ? - var attrinbuteLists = interfaceDeclarationSyntax.AttributeLists.FirstOrDefault(x => x.Attributes.Any(a => a.Name.ToString().Equals("ProxyInterfaceGenerator.Proxy"))); - if (attrinbuteLists == null) - { - return false; - } - - var attributeSyntax = attrinbuteLists.Attributes.FirstOrDefault(); - if (attributeSyntax == null) - { - return false; - } - - var arg = attributeSyntax.ArgumentList.Arguments.First(); - typeName = arg.Expression.ChildNodes().First().GetText().ToString(); - return true; - } - } -} diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs new file mode 100644 index 0000000..5334aed --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs @@ -0,0 +1,9 @@ +namespace ProxyInterfaceSourceGenerator.SyntaxReceiver +{ + internal record ProxyData + { + public string TypeName { get; init; } + + public bool ProxyAll { get; init; } + } +} \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs new file mode 100644 index 0000000..42b22fa --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ProxyInterfaceSourceGenerator.SyntaxReceiver +{ + internal class ProxySyntaxReceiver : ISyntaxReceiver + { + public IDictionary CandidateInterfaces { get; } = new Dictionary(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is InterfaceDeclarationSyntax interfaceDeclarationSyntax && TryGet(interfaceDeclarationSyntax, out var data)) + { + CandidateInterfaces.Add(interfaceDeclarationSyntax, data); + } + } + + private static bool TryGet(InterfaceDeclarationSyntax interfaceDeclarationSyntax, out ProxyData data) + { + data = new(); + + // TODO : how to check if the InterfaceDeclarationSyntax has 'partial' ? + var attributeLists = interfaceDeclarationSyntax.AttributeLists.FirstOrDefault(x => x.Attributes.Any(a => a.Name.ToString().Equals("ProxyInterfaceGenerator.Proxy"))); + if (attributeLists is null) + { + return false; + } + + var args = attributeLists.Attributes.FirstOrDefault()?.ArgumentList; + if (args is null) + { + return false; + } + + data = new() + { + TypeName = args.Arguments[0].Expression.ChildNodes().First().GetText().ToString(), + ProxyAll = bool.Parse(args.Arguments[1].Expression.ChildNodes().First().GetText().ToString()) + }; + + return true; + } + } +}