From 7baf050c127326ee5bc51bead54329eb012b91b3 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 2 Aug 2021 11:49:41 +0200 Subject: [PATCH] Add support for using a simple type-name (#22) --- .../ProxyInterfaceConsumer/Address.cs | 2 +- .../ProxyInterfaceConsumer/IAddress.cs | 8 ++++-- src-examples/ProxyInterfaceConsumer/Person.cs | 3 ++- .../Compatibility/NullableAttributes.cs | 18 +++++++++++++ .../Extensions/SyntaxNodeUtils.cs | 5 ++-- .../FileGenerators/BaseGenerator.cs | 25 ++++++++++++----- .../PartialInterfacesGenerator.cs | 2 +- .../FileGenerators/ProxyClassesGenerator.cs | 2 +- .../SyntaxReceiver/ProxyData.cs | 6 +++-- .../SyntaxReceiver/ProxySyntaxReceiver.cs | 27 ++++++++++++++----- 10 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 src/ProxyInterfaceSourceGenerator/Compatibility/NullableAttributes.cs diff --git a/src-examples/ProxyInterfaceConsumer/Address.cs b/src-examples/ProxyInterfaceConsumer/Address.cs index c4db154..0d15551 100644 --- a/src-examples/ProxyInterfaceConsumer/Address.cs +++ b/src-examples/ProxyInterfaceConsumer/Address.cs @@ -1,4 +1,4 @@ -namespace ProxyInterfaceConsumer +namespace DifferentNamespace { public class Address { diff --git a/src-examples/ProxyInterfaceConsumer/IAddress.cs b/src-examples/ProxyInterfaceConsumer/IAddress.cs index d44b0f6..70b8fcb 100644 --- a/src-examples/ProxyInterfaceConsumer/IAddress.cs +++ b/src-examples/ProxyInterfaceConsumer/IAddress.cs @@ -1,6 +1,10 @@ -namespace ProxyInterfaceConsumer +using DifferentNamespace; +using System; +using System.Linq; + +namespace ProxyInterfaceConsumer { - // [ProxyInterfaceGenerator.Proxy(typeof(ProxyInterfaceConsumer.Address))] + [ProxyInterfaceGenerator.Proxy(typeof(Address))] public partial interface IAddress { } diff --git a/src-examples/ProxyInterfaceConsumer/Person.cs b/src-examples/ProxyInterfaceConsumer/Person.cs index 84efcc1..09c0e34 100644 --- a/src-examples/ProxyInterfaceConsumer/Person.cs +++ b/src-examples/ProxyInterfaceConsumer/Person.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using DifferentNamespace; +using System.Collections.Generic; using System.Threading.Tasks; namespace ProxyInterfaceConsumer diff --git a/src/ProxyInterfaceSourceGenerator/Compatibility/NullableAttributes.cs b/src/ProxyInterfaceSourceGenerator/Compatibility/NullableAttributes.cs new file mode 100644 index 0000000..82dfeb2 --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/Compatibility/NullableAttributes.cs @@ -0,0 +1,18 @@ +// https://stackoverflow.com/questions/61573959/how-to-resolve-error-notnullwhen-attribute-is-inaccessible-due-to-its-protectio + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } +} \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/SyntaxNodeUtils.cs b/src/ProxyInterfaceSourceGenerator/Extensions/SyntaxNodeUtils.cs index fb2c780..a8bed5e 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/SyntaxNodeUtils.cs +++ b/src/ProxyInterfaceSourceGenerator/Extensions/SyntaxNodeUtils.cs @@ -1,11 +1,12 @@ using Microsoft.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace ProxyInterfaceSourceGenerator.Extensions { internal static class SyntaxNodeUtils { // https://stackoverflow.com/questions/20458457/getting-class-fullname-including-namespace-from-roslyn-classdeclarationsyntax - public static bool TryGetParentSyntax(this SyntaxNode? syntaxNode, out T? result) where T : SyntaxNode + public static bool TryGetParentSyntax(this SyntaxNode? syntaxNode, [NotNullWhen(true)] out T? result) where T : SyntaxNode { result = null; @@ -25,7 +26,7 @@ namespace ProxyInterfaceSourceGenerator.Extensions if (syntaxNode.GetType() == typeof(T)) { - result = syntaxNode as T; + result = (T)syntaxNode; return true; } diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs index 36bcec8..a570541 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; @@ -67,16 +68,28 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators return typeSymbolAsString; } - protected INamedTypeSymbol GetNamedTypeSymbolByFullName(string fullName) + protected INamedTypeSymbol GetNamedTypeSymbolByFullName(string name, IEnumerable? usings = null) { // 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(fullName); - if (symbol is null) - { - throw new Exception($"The type '{fullName}' is not found."); + var symbol = _context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name); + if (symbol is not null) + { + return symbol; + } + + if (usings is not null) + { + foreach (var @using in usings) + { + symbol = _context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName($"{@using}.{name}"); + if (symbol is not null) + { + return symbol; + } + } } - return symbol; + throw new Exception($"The type '{name}' is not found."); } } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs index 301dc4a..ee422dc 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -26,7 +26,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators private FileData GenerateFile(ProxyData pd) { - var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName); + var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName, pd.Usings); var interfaceName = targetClassSymbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName); var file = new FileData( diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs index 202731a..fdb69ac 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -26,7 +26,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators private FileData GenerateFile(ProxyData pd) { - var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName); + var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName, pd.Usings); var interfaceName = targetClassSymbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName); var className = targetClassSymbol.ResolveProxyClassName(); var constructorName = $"{targetClassSymbol.Name}Proxy"; diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs index 411b5c7..9729900 100644 --- a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs +++ b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxyData.cs @@ -1,6 +1,8 @@ -namespace ProxyInterfaceSourceGenerator.SyntaxReceiver +using System.Collections.Generic; + +namespace ProxyInterfaceSourceGenerator.SyntaxReceiver { - internal record ProxyData(string Namespace, string InterfaceName, string RawTypeName, string TypeName, bool ProxyAll) + internal record ProxyData(string Namespace, string InterfaceName, string RawTypeName, string TypeName, List Usings, bool ProxyAll) { public string FileName => TypeName.Replace('.', '_').Replace('`', '_'); } diff --git a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs index 7b9e6a8..0047e60 100644 --- a/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs +++ b/src/ProxyInterfaceSourceGenerator/SyntaxReceiver/ProxySyntaxReceiver.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -20,9 +21,9 @@ namespace ProxyInterfaceSourceGenerator.SyntaxReceiver } } - private static bool TryGet(InterfaceDeclarationSyntax interfaceDeclarationSyntax, out ProxyData data) + private static bool TryGet(InterfaceDeclarationSyntax interfaceDeclarationSyntax, [NotNullWhen(true)] out ProxyData? data) { - data = new(string.Empty, string.Empty, string.Empty, string.Empty, false); + data = null; if (interfaceDeclarationSyntax.Modifiers.Select(m => m.ToString()).Except(Modifiers).Count() != 0) { @@ -42,27 +43,39 @@ namespace ProxyInterfaceSourceGenerator.SyntaxReceiver return false; } + var usings = new List(); + string ns = string.Empty; - if (SyntaxNodeUtils.TryGetParentSyntax(interfaceDeclarationSyntax, out NamespaceDeclarationSyntax namespaceDeclarationSyntax)) + if (SyntaxNodeUtils.TryGetParentSyntax(interfaceDeclarationSyntax, out NamespaceDeclarationSyntax? namespaceDeclarationSyntax)) { ns = namespaceDeclarationSyntax.Name.ToString(); + usings.Add(ns); + } + + if (SyntaxNodeUtils.TryGetParentSyntax(interfaceDeclarationSyntax, out CompilationUnitSyntax? cc)) + { + foreach (var @using in cc.Usings) + { + usings.Add(@using.Name.ToString()); + } } - string rawTypename = ((TypeOfExpressionSyntax)argumentList.Arguments[0].Expression).Type.ToString(); + string rawTypeName = ((TypeOfExpressionSyntax)argumentList.Arguments[0].Expression).Type.ToString(); data = new ( ns, interfaceDeclarationSyntax.Identifier.ToString(), - rawTypename, - ResolveType(rawTypename), + rawTypeName, + ConvertTypeName(rawTypeName), + usings, false //bool.Parse(argumentList.Arguments[1].Expression.GetText().ToString()) ); return true; } - private static string ResolveType(string typeName) + private static string ConvertTypeName(string typeName) { return !(typeName.Contains('<') && typeName.Contains('>')) ? typeName :