diff --git a/.gitignore b/.gitignore index fbb909c..c553a41 100644 --- a/.gitignore +++ b/.gitignore @@ -316,3 +316,4 @@ __pycache__/ /.aider.tags.cache.v3/cache.db-shm /.aider.tags.cache.v3/cache.db-wal /.aider.input.history +.nuget-cache/ diff --git a/Directory.Build.props b/Directory.Build.props index ec6c451..fde08c3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -15,6 +15,9 @@ $(PackagesDirectory) + + $(RepositoryRoot).nuget-cache/ + true diff --git a/Directory.Packages.props b/Directory.Packages.props index 1e37705..d7363a6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,6 +1,6 @@ - + diff --git a/source/Directory.Build.props b/source/Directory.Build.props index 93c8893..0dbb300 100644 --- a/source/Directory.Build.props +++ b/source/Directory.Build.props @@ -4,7 +4,7 @@ - 1.0.0-beta.6 + 1.0.0-beta.7 Steven T. Cramer TimeWarp.SourceGenerators TimeWarp.SourceGenerators diff --git a/source/timewarp-source-generators/interface-delegation-generator.cs b/source/timewarp-source-generators/interface-delegation-generator.cs index 1c54c17..6864b40 100644 --- a/source/timewarp-source-generators/interface-delegation-generator.cs +++ b/source/timewarp-source-generators/interface-delegation-generator.cs @@ -267,12 +267,59 @@ private static List GenerateInterfaceDelegation( SemanticModel semanticModel) { var code = new List(); + var processedMembers = new HashSet(); - // Get all members of the interface + // Process the interface and all inherited interfaces + if (interfaceType is INamedTypeSymbol namedInterface) + { + ProcessInterfaceMembers(namedInterface, delegateMemberName, classSymbol, code, processedMembers); + } + + return code; + } + + private static void ProcessInterfaceMembers( + INamedTypeSymbol interfaceType, + string delegateMemberName, + INamedTypeSymbol classSymbol, + List code, + HashSet processedMembers) + { + // First, recursively process all base interfaces + foreach (INamedTypeSymbol baseInterface in interfaceType.AllInterfaces) + { + ProcessInterfaceMembersCore(baseInterface, delegateMemberName, classSymbol, code, processedMembers); + } + + // Then process the current interface's own members + ProcessInterfaceMembersCore(interfaceType, delegateMemberName, classSymbol, code, processedMembers); + } + + private static void ProcessInterfaceMembersCore( + INamedTypeSymbol interfaceType, + string delegateMemberName, + INamedTypeSymbol classSymbol, + List code, + HashSet processedMembers) + { ImmutableArray interfaceMembers = interfaceType.GetMembers(); foreach (ISymbol member in interfaceMembers) { + // Create a unique key for this member to avoid duplicates + string memberKey = $"{member.Name}_{member.Kind}"; + if (member is IMethodSymbol method) + { + // Include return type and parameter types in key to handle overloads and different return types + string returnType = method.ReturnType.ToDisplayString(); + string paramTypes = string.Join(",", method.Parameters.Select(p => p.Type.ToDisplayString())); + memberKey = $"{member.Name}_{method.MethodKind}_{returnType}_{paramTypes}"; + } + + // Skip if we've already processed this member + if (!processedMembers.Add(memberKey)) + continue; + // Skip if the class already implements this member explicitly ISymbol? existingImplementation = classSymbol.GetMembers(member.Name) .FirstOrDefault(m => !m.IsImplicitlyDeclared); @@ -282,8 +329,8 @@ private static List GenerateInterfaceDelegation( switch (member) { - case IMethodSymbol method when method.MethodKind == MethodKind.Ordinary: - code.Add(GenerateMethodDelegation(method, delegateMemberName)); + case IMethodSymbol methodSymbol when methodSymbol.MethodKind == MethodKind.Ordinary: + code.Add(GenerateMethodDelegation(methodSymbol, delegateMemberName)); break; case IPropertySymbol property: @@ -295,8 +342,6 @@ private static List GenerateInterfaceDelegation( break; } } - - return code; } private static string GenerateMethodDelegation(IMethodSymbol method, string delegateMemberName) @@ -375,7 +420,7 @@ private static string GenerateSourceFile( builder.AppendLine(); } - builder.AppendLine($"// Interface delegation for {className}"); + builder.AppendLine($"// Interface delegation for {className} - YO YO YO MAMA v2 with inherited interfaces!"); builder.AppendLine($"public partial class {className}"); builder.AppendLine("{"); diff --git a/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/DataService.implements.g.cs b/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/DataService.implements.g.cs index 1a4e45f..9f17241 100644 --- a/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/DataService.implements.g.cs +++ b/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/DataService.implements.g.cs @@ -3,7 +3,7 @@ namespace TimeWarp.SourceGenerators.TestConsole; -// Interface delegation for DataService +// Interface delegation for DataService - YO YO YO MAMA v2 with inherited interfaces! public partial class DataService { // Delegation to _logger for TimeWarp.SourceGenerators.TestConsole.ILogger diff --git a/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/StringListWrapper.implements.g.cs b/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/StringListWrapper.implements.g.cs new file mode 100644 index 0000000..f09f73f --- /dev/null +++ b/tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/StringListWrapper.implements.g.cs @@ -0,0 +1,70 @@ +// +#nullable enable + +namespace TimeWarp.SourceGenerators.TestConsole; + +// Interface delegation for StringListWrapper - YO YO YO MAMA v2 with inherited interfaces! +public partial class StringListWrapper +{ + // Delegation to InnerList for System.Collections.Generic.IList + public void Add(string item) + { + InnerList.Add(item); + } + + public void Clear() + { + InnerList.Clear(); + } + + public bool Contains(string item) + { + return InnerList.Contains(item); + } + + public void CopyTo(string[] array, int arrayIndex) + { + InnerList.CopyTo(array, arrayIndex); + } + + public bool Remove(string item) + { + return InnerList.Remove(item); + } + + public int Count + { + get => InnerList.Count; + } + + public bool IsReadOnly + { + get => InnerList.IsReadOnly; + } + + public System.Collections.Generic.IEnumerator GetEnumerator() + { + return InnerList.GetEnumerator(); + } + + public int IndexOf(string item) + { + return InnerList.IndexOf(item); + } + + public void Insert(int index, string item) + { + InnerList.Insert(index, item); + } + + public void RemoveAt(int index) + { + InnerList.RemoveAt(index); + } + + public string this[int index] + { + get => InnerList[index]; set => InnerList[index] = value; + } + +}