Do not redefine interfaces. (#67)
* Do not redefine interfaces. * Improve code style, readability and performance after initial code review * Implemented unit tests for the interface inheritance * Cleaned up unit tests. * Completely remove output helper.
This commit is contained in:
@@ -60,4 +60,41 @@ internal static class NamedTypeSymbolExtensions
|
||||
$"{namedTypeSymbol}Proxy" :
|
||||
$"{namedTypeSymbol}Proxy<{string.Join(", ", namedTypeSymbol.TypeArguments.Select(ta => ta.Name))}>";
|
||||
}
|
||||
|
||||
public static List<INamedTypeSymbol> ResolveImplementedInterfaces(this INamedTypeSymbol symbol, bool proxyBaseClasses)
|
||||
{
|
||||
//Members implemented by us or base classes should go here.
|
||||
var publicMembers = symbol.GetMembers().Where(m => m.DeclaredAccessibility == Accessibility.Public).ToList();
|
||||
//Direct interfaces, recursive interfaces or base class interfaces should go here.
|
||||
var interfaces = new List<INamedTypeSymbol>(symbol.Interfaces);
|
||||
var baseType = symbol.BaseType;
|
||||
while (proxyBaseClasses && baseType != null && baseType.SpecialType != SpecialType.System_Object)
|
||||
{
|
||||
publicMembers.AddRange(baseType.GetMembers().Where(m => m.DeclaredAccessibility == Accessibility.Public));
|
||||
interfaces.AddRange(baseType.Interfaces);
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
|
||||
//Filter explicitly implemented interfaces.
|
||||
var realizedInterfaces = new List<INamedTypeSymbol>();
|
||||
foreach (var iface in interfaces)
|
||||
{
|
||||
var isRealized = true;
|
||||
var allMembers = iface.AllInterfaces.Aggregate(iface.GetMembers(), (xs, x) => xs.AddRange(x.GetMembers()));
|
||||
foreach (var member in allMembers)
|
||||
{
|
||||
var implementation = symbol.FindImplementationForInterfaceMember(member);
|
||||
if (!publicMembers.Contains(implementation!))
|
||||
{
|
||||
isRealized = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRealized)
|
||||
{
|
||||
realizedInterfaces.Add(iface);
|
||||
}
|
||||
}
|
||||
return realizedInterfaces;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators;
|
||||
|
||||
internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
|
||||
{
|
||||
private IReadOnlyCollection<INamedTypeSymbol> ImplementedInterfaces = new List<INamedTypeSymbol>();
|
||||
public PartialInterfacesGenerator(Context context, bool supportsNullable) :
|
||||
base(context, supportsNullable)
|
||||
{
|
||||
@@ -58,6 +59,9 @@ internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
|
||||
ProxyData proxyData)
|
||||
{
|
||||
var extendsProxyClasses = GetExtendsProxyData(proxyData, classSymbol);
|
||||
ImplementedInterfaces = classSymbol.Symbol.ResolveImplementedInterfaces(proxyData.ProxyBaseClasses);
|
||||
var implementedInterfacesNames = ImplementedInterfaces.Select(i => i.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat));
|
||||
var implements = implementedInterfacesNames.Any() ? $" : {string.Join(", ", implementedInterfacesNames)}" : string.Empty;
|
||||
var @new = extendsProxyClasses.Any() ? "new " : string.Empty;
|
||||
var (namespaceStart, namespaceEnd) = NamespaceBuilder.Build(ns);
|
||||
|
||||
@@ -74,7 +78,7 @@ internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
|
||||
using System;
|
||||
|
||||
{namespaceStart}
|
||||
public partial interface {interfaceName}
|
||||
public partial interface {interfaceName}{implements}
|
||||
{{
|
||||
{@new}{classSymbol.Symbol} _Instance {{ get; }}
|
||||
|
||||
@@ -88,11 +92,26 @@ using System;
|
||||
{SupportsNullable.IIf("#nullable restore")}";
|
||||
}
|
||||
|
||||
private Func<T, bool> InterfaceFilter<T>() where T : ISymbol
|
||||
{
|
||||
var hashSet = new HashSet<string>();
|
||||
foreach (var iface in ImplementedInterfaces)
|
||||
{
|
||||
var members = iface.AllInterfaces.Aggregate(iface.GetMembers(), (xs, x) => xs.AddRange(x.GetMembers()));
|
||||
foreach (var member in members)
|
||||
{
|
||||
hashSet.Add(member.Name);
|
||||
}
|
||||
}
|
||||
//Member is not already implemented in another interface.
|
||||
return (T t) => !hashSet.Contains(t.Name);
|
||||
}
|
||||
|
||||
private string GenerateProperties(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
|
||||
foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyBaseClasses))
|
||||
foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyBaseClasses, InterfaceFilter<IPropertySymbol>()))
|
||||
{
|
||||
var type = GetPropertyType(property, out var isReplaced);
|
||||
|
||||
@@ -125,7 +144,7 @@ using System;
|
||||
private string GenerateMethods(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
foreach (var method in MemberHelper.GetPublicMethods(targetClassSymbol, proxyBaseClasses))
|
||||
foreach (var method in MemberHelper.GetPublicMethods(targetClassSymbol, proxyBaseClasses, InterfaceFilter<IMethodSymbol>()))
|
||||
{
|
||||
var methodParameters = GetMethodParameters(method.Parameters, true);
|
||||
var whereStatement = GetWhereStatementFromMethod(method);
|
||||
@@ -145,7 +164,7 @@ using System;
|
||||
private string GenerateEvents(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyBaseClasses))
|
||||
foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyBaseClasses, InterfaceFilter<IMethodSymbol>()))
|
||||
{
|
||||
var ps = @event.First().Parameters.First();
|
||||
var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString();
|
||||
|
||||
@@ -82,7 +82,7 @@ internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
|
||||
var operators = GenerateOperators(targetClassSymbol, pd.ProxyBaseClasses);
|
||||
|
||||
var configurationForMapster = string.Empty;
|
||||
if (Context.ReplacedTypes.Any())
|
||||
if (Context.ReplacedTypes.Count > 0)
|
||||
{
|
||||
configurationForMapster = GenerateMapperConfigurationForMapster();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user