From 44dff0c855ee12f4638a9cdafd00dee7d5c54853 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Fri, 17 Oct 2025 23:33:55 +0700 Subject: [PATCH 1/2] Fix interface delegation to include inherited interface members Fixes #27 - Process all inherited interfaces recursively using AllInterfaces - Add ProcessInterfaceMembers helper to traverse interface hierarchy - Include return type in method key to handle overloaded GetEnumerator - Add local NuGet cache to avoid global cache issues - Add version marker comment for debugging (YO YO YO MAMA v2) - Update .gitignore to exclude .nuget-cache/ The fix now generates ALL interface members including those from base interfaces. For example, IList now generates: - IList members: IndexOf, Insert, RemoveAt, this[int] - ICollection members: Add, Clear, Contains, CopyTo, Remove, Count, IsReadOnly - IEnumerable members: GetEnumerator Known limitation: IEnumerable (non-generic) GetEnumerator() requires explicit interface implementation due to return type conflict. This will be addressed in a future update. --- .gitignore | 1 + Directory.Build.props | 3 + .../interface-delegation-generator.cs | 57 +++++++++++++-- .../DataService.implements.g.cs | 2 +- .../StringListWrapper.implements.g.cs | 70 +++++++++++++++++++ 5 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 tests/timewarp-source-generators-test-console/generated/timewarp-source-generators/TimeWarp.SourceGenerators.InterfaceDelegationGenerator/StringListWrapper.implements.g.cs 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/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; + } + +} From 3fe50be357602a1f5c24522767482c173a86745d Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Fri, 17 Oct 2025 23:53:27 +0700 Subject: [PATCH 2/2] Bump version to beta.7 and use variable for package version - Update version from beta.6 to beta.7 - Change Directory.Packages.props to use $(Version) variable - Single source of truth for version in source/Directory.Build.props --- Directory.Packages.props | 2 +- source/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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