From 054d9b00dc72e7d76deb22892df66bb21d227f46 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder <2490482+GrahamTheCoder@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:11:05 +0000 Subject: [PATCH 1/7] Fix property backing field conversion for virtual properties This commit fixes issue #827 where accessing the auto-generated backing field (e.g. `_Prop`) of an overridable (virtual) property in VB.NET was incorrectly converted to the virtual property access (`Prop`) in C#. It now correctly maps these accesses to the explicitly generated non-virtual backing property `MyClassProp` and ensures that the MyClassProp property is correctly generated when such an access occurs. A test case has been added to MemberTests.cs to verify the correct translation of such field access. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- CodeConverter/CSharp/CommonConversions.cs | 3 ++ .../CSharp/DeclarationNodeVisitor.cs | 15 +++++- Tests/VB/MemberTests.cs | 47 ++++++++++++++++++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs index 6647937fe..ef50db057 100644 --- a/CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -294,6 +294,9 @@ public SyntaxToken ConvertIdentifier(SyntaxToken id, bool isAttribute = false, S text = "value"; } else if (normalizedText.StartsWith("_", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol propertyFieldSymbol && propertyFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Property) == true) { text = propertyFieldSymbol.AssociatedSymbol.Name; + if (propertyFieldSymbol.AssociatedSymbol.IsVirtual && !propertyFieldSymbol.AssociatedSymbol.IsAbstract) { + text = "MyClass" + text; + } } else if (normalizedText.EndsWith("Event", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol eventFieldSymbol && eventFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Event) == true) { text = eventFieldSymbol.AssociatedSymbol.Name; } else if (WinformsConversions.MayNeedToInlinePropertyAccess(id.Parent, idSymbol) && _typeContext.HandledEventsAnalysis.ShouldGeneratePropertyFor(idSymbol.Name)) { diff --git a/CodeConverter/CSharp/DeclarationNodeVisitor.cs b/CodeConverter/CSharp/DeclarationNodeVisitor.cs index f6fd6709e..54642321b 100644 --- a/CodeConverter/CSharp/DeclarationNodeVisitor.cs +++ b/CodeConverter/CSharp/DeclarationNodeVisitor.cs @@ -336,7 +336,7 @@ var dummyClass public override async Task VisitClassBlock(VBSyntax.ClassBlockSyntax node) { - _accessorDeclarationNodeConverter.AccessedThroughMyClass = GetMyClassAccessedNames(node); + _accessorDeclarationNodeConverter.AccessedThroughMyClass = GetMyClassAccessedNames(node, _semanticModel); var classStatement = node.ClassStatement; var attributes = await CommonConversions.ConvertAttributesAsync(classStatement.AttributeLists); var (parameters, constraints) = await SplitTypeParametersAsync(classStatement.TypeParameterList); @@ -689,12 +689,23 @@ private static async Task ConvertStatementsAsync(SyntaxList (IEnumerable) await s.Accept(methodBodyVisitor))); } - private static HashSet GetMyClassAccessedNames(VBSyntax.ClassBlockSyntax classBlock) + private static HashSet GetMyClassAccessedNames(VBSyntax.ClassBlockSyntax classBlock, SemanticModel semanticModel) { var memberAccesses = classBlock.DescendantNodes().OfType(); var accessedTextNames = new HashSet(memberAccesses .Where(mae => mae.Expression is VBSyntax.MyClassExpressionSyntax) .Select(mae => mae.Name.Identifier.Text), StringComparer.OrdinalIgnoreCase); + + var identifierNames = classBlock.DescendantNodes().OfType().Where(id => id.Identifier.Text.StartsWith("_", StringComparison.OrdinalIgnoreCase)); + foreach (var id in identifierNames) + { + var symbolInfo = semanticModel.GetSymbolInfo(id); + if (symbolInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.AssociatedSymbol != null && fieldSymbol.AssociatedSymbol.IsVirtual && !fieldSymbol.AssociatedSymbol.IsAbstract) + { + accessedTextNames.Add(fieldSymbol.AssociatedSymbol.Name); + } + } + return accessedTextNames; } diff --git a/Tests/VB/MemberTests.cs b/Tests/VB/MemberTests.cs index 03bd4dd0b..7aab41005 100644 --- a/Tests/VB/MemberTests.cs +++ b/Tests/VB/MemberTests.cs @@ -1495,4 +1495,49 @@ End Sub End Class"); } -} \ No newline at end of file + + [Fact] + public async Task TestMyClassPropertyAccess() + { + await TestConversionVisualBasicToCSharpAsync(@" +Class Foo + Overridable Property Prop As Integer = 5 + + Sub Test() + _Prop = 10 ' This should convert to MyClassProp = 10 not to Prop = 10 + Dim isCorrect = MyClass.Prop = 10 ' After conversion this will return 5instead of 10 because we wrote to Child.Prop + End Sub +End Class +Class Child + Inherits Foo + Overrides Property Prop As Integer = 20 +End Class", @" +internal partial class Foo +{ + public int MyClassProp { get; set; } = 5; + + public virtual int Prop + { + get + { + return MyClassProp; + } + + set + { + MyClassProp = value; + } + } + + public void Test() + { + MyClassProp = 10; // This should convert to MyClassProp = 10 not to Prop = 10 + bool isCorrect = MyClassProp == 10; // After conversion this will return 5instead of 10 because we wrote to Child.Prop + } +} +internal partial class Child : Foo +{ + public override int Prop { get; set; } = 20; +}"); + } +} From 41ba01d7c8608e056e3020f40ae72bdac8e3c4c0 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder <2490482+GrahamTheCoder@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:43:34 +0000 Subject: [PATCH 2/7] Fix property backing field conversion for virtual properties This commit fixes issue #827 where accessing the auto-generated backing field (e.g. `_Prop`) of an overridable (virtual) property in VB.NET was incorrectly converted to the virtual property access (`Prop`) in C#. It now correctly maps these accesses to the explicitly generated non-virtual backing property `MyClassProp` and ensures that the MyClassProp property is correctly generated when such an access occurs. A test case has been added to MemberTests.cs to verify the correct translation of such field access. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- CodeConverter/CSharp/DeclarationNodeVisitor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeConverter/CSharp/DeclarationNodeVisitor.cs b/CodeConverter/CSharp/DeclarationNodeVisitor.cs index 54642321b..9bca1b344 100644 --- a/CodeConverter/CSharp/DeclarationNodeVisitor.cs +++ b/CodeConverter/CSharp/DeclarationNodeVisitor.cs @@ -700,7 +700,7 @@ private static HashSet GetMyClassAccessedNames(VBSyntax.ClassBlockSyntax foreach (var id in identifierNames) { var symbolInfo = semanticModel.GetSymbolInfo(id); - if (symbolInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.AssociatedSymbol != null && fieldSymbol.AssociatedSymbol.IsVirtual && !fieldSymbol.AssociatedSymbol.IsAbstract) + if (symbolInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.AssociatedSymbol != null && fieldSymbol.AssociatedSymbol.IsVirtual && !fieldSymbol.AssociatedSymbol.IsAbstract && !id.Identifier.Text.StartsWith("MyClass", StringComparison.OrdinalIgnoreCase)) { accessedTextNames.Add(fieldSymbol.AssociatedSymbol.Name); } From 9ed5ef67d59f9523c4372d043e9d34932e68ac2f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 21:55:29 +0000 Subject: [PATCH 3/7] Fix #827: VB backing field _Prop correctly maps to MyClassProp for virtual auto-properties When VB accesses _Prop (the backing field of a virtual auto-property), the converter now emits MyClassProp instead of Prop, matching the C# backing property name and avoiding virtual dispatch to child class overrides. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- CodeConverter/CSharp/CommonConversions.cs | 8 ++-- .../CSharp/DeclarationNodeVisitor.cs | 18 ++++---- Tests/CSharp/MemberTests/MemberTests.cs | 45 +++++++++++++++++++ 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs index ef50db057..ef4b39464 100644 --- a/CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -293,10 +293,10 @@ public SyntaxToken ConvertIdentifier(SyntaxToken id, bool isAttribute = false, S // AND the first explicitly declared parameter is this symbol, we need to replace it with value. text = "value"; } else if (normalizedText.StartsWith("_", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol propertyFieldSymbol && propertyFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Property) == true) { - text = propertyFieldSymbol.AssociatedSymbol.Name; - if (propertyFieldSymbol.AssociatedSymbol.IsVirtual && !propertyFieldSymbol.AssociatedSymbol.IsAbstract) { - text = "MyClass" + text; - } +// For virtual auto-properties, VB backing field _Prop maps to the C# MyClassProp backing property + text = propertyFieldSymbol.IsImplicitlyDeclared && propertyFieldSymbol.AssociatedSymbol is IPropertySymbol { IsVirtual: true, IsAbstract: false } vProp + ? "MyClass" + vProp.Name + : propertyFieldSymbol.AssociatedSymbol.Name; } else if (normalizedText.EndsWith("Event", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol eventFieldSymbol && eventFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Event) == true) { text = eventFieldSymbol.AssociatedSymbol.Name; } else if (WinformsConversions.MayNeedToInlinePropertyAccess(id.Parent, idSymbol) && _typeContext.HandledEventsAnalysis.ShouldGeneratePropertyFor(idSymbol.Name)) { diff --git a/CodeConverter/CSharp/DeclarationNodeVisitor.cs b/CodeConverter/CSharp/DeclarationNodeVisitor.cs index 9bca1b344..a999cac40 100644 --- a/CodeConverter/CSharp/DeclarationNodeVisitor.cs +++ b/CodeConverter/CSharp/DeclarationNodeVisitor.cs @@ -689,20 +689,22 @@ private static async Task ConvertStatementsAsync(SyntaxList (IEnumerable) await s.Accept(methodBodyVisitor))); } - private static HashSet GetMyClassAccessedNames(VBSyntax.ClassBlockSyntax classBlock, SemanticModel semanticModel) + private HashSet GetMyClassAccessedNames(VBSyntax.ClassBlockSyntax classBlock) { var memberAccesses = classBlock.DescendantNodes().OfType(); var accessedTextNames = new HashSet(memberAccesses .Where(mae => mae.Expression is VBSyntax.MyClassExpressionSyntax) .Select(mae => mae.Name.Identifier.Text), StringComparer.OrdinalIgnoreCase); - var identifierNames = classBlock.DescendantNodes().OfType().Where(id => id.Identifier.Text.StartsWith("_", StringComparison.OrdinalIgnoreCase)); - foreach (var id in identifierNames) - { - var symbolInfo = semanticModel.GetSymbolInfo(id); - if (symbolInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.AssociatedSymbol != null && fieldSymbol.AssociatedSymbol.IsVirtual && !fieldSymbol.AssociatedSymbol.IsAbstract && !id.Identifier.Text.StartsWith("MyClass", StringComparison.OrdinalIgnoreCase)) - { - accessedTextNames.Add(fieldSymbol.AssociatedSymbol.Name); + // Also treat direct backing field access (_Prop) as MyClass access for virtual auto-properties. + // In VB, writing _Prop directly accesses the backing field, bypassing virtual dispatch - + // the same semantics as MyClass.Prop. In C#, these virtual properties get a MyClassProp + // backing property, so _Prop must map to MyClassProp. + var backingFieldIdentifiers = classBlock.DescendantNodes().OfType() + .Where(id => id.Identifier.ValueText.StartsWith("_", StringComparison.OrdinalIgnoreCase)); + foreach (var id in backingFieldIdentifiers) { + if (_semanticModel.GetSymbolInfo(id).Symbol is IFieldSymbol { IsImplicitlyDeclared: true, AssociatedSymbol: IPropertySymbol { IsVirtual: true, IsAbstract: false } associatedProp }) { + accessedTextNames.Add(associatedProp.Name); } } diff --git a/Tests/CSharp/MemberTests/MemberTests.cs b/Tests/CSharp/MemberTests/MemberTests.cs index da604f2dc..8507d5086 100644 --- a/Tests/CSharp/MemberTests/MemberTests.cs +++ b/Tests/CSharp/MemberTests/MemberTests.cs @@ -1576,4 +1576,49 @@ private void OptionalByRefWithDefault([Optional][DefaultParameterValue(""a"")] r CS7036: There is no argument given that corresponds to the required parameter 'str1' of 'MissingByRefArgumentWithNoExplicitDefaultValue.ByRefNoDefault(ref string)' "); } + + [Fact] + public async Task TestCharDefaultValueForStringParameterAsync() + { + // Issue #557: VB allows Char as a default value for a String parameter, but C# does not. + // The fix replaces the default with null and prepends a null-coalescing assignment in the body. + await TestConversionVisualBasicToCSharpAsync( + @"Module TestModule + Friend Const DlM As Char = ""^""c + + Friend Function LeftSideOf(Optional ByVal strDlM As String = DlM) As String + Return strDlM + End Function +End Module", @" +internal static partial class TestModule +{ + internal const char DlM = '^'; + + internal static string LeftSideOf(string strDlM = null) + { + strDlM = strDlM ?? DlM.ToString(); + return strDlM; + } +}"); + } + + [Fact] + public async Task TestCharLiteralDefaultValueForStringParameterAsync() + { + // Issue #557: inline char literal as default value for a String parameter. + await TestConversionVisualBasicToCSharpAsync( + @"Class TestClass + Friend Function Foo(Optional s As String = ""^""c) As String + Return s + End Function +End Class", @" +internal partial class TestClass +{ + internal string Foo(string s = null) + { + s = s ?? '^'.ToString(); + return s; + } +}"); + } } \ No newline at end of file From f29af8b16750cf810a81a707ff0534b1894d05c6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 22:04:33 +0000 Subject: [PATCH 4/7] Add regression test for #827 in correct location, fix comment indentation - Add TestOverridableAutoPropertyBackingFieldAccessAsync to Tests/CSharp/MemberTests/PropertyMemberTests.cs (VB->CS tests) covering the exact issue #827 scenario: _Prop = 10 converts to MyClassProp = 10 - Remove duplicate test from Tests/VB/MemberTests.cs where VB->CS tests do not belong - Fix indentation of comment in CommonConversions.cs https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- CodeConverter/CSharp/CommonConversions.cs | 4 +- .../CSharp/MemberTests/PropertyMemberTests.cs | 49 ++++++++++++++++++- Tests/VB/MemberTests.cs | 47 +----------------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs index ef4b39464..c30e2edce 100644 --- a/CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Linq.Expressions; using System.Runtime.CompilerServices; using ICSharpCode.CodeConverter.Util.FromRoslyn; @@ -293,7 +293,7 @@ public SyntaxToken ConvertIdentifier(SyntaxToken id, bool isAttribute = false, S // AND the first explicitly declared parameter is this symbol, we need to replace it with value. text = "value"; } else if (normalizedText.StartsWith("_", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol propertyFieldSymbol && propertyFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Property) == true) { -// For virtual auto-properties, VB backing field _Prop maps to the C# MyClassProp backing property + // For virtual auto-properties, VB backing field _Prop maps to the C# MyClassProp backing property (bypasses virtual dispatch) text = propertyFieldSymbol.IsImplicitlyDeclared && propertyFieldSymbol.AssociatedSymbol is IPropertySymbol { IsVirtual: true, IsAbstract: false } vProp ? "MyClass" + vProp.Name : propertyFieldSymbol.AssociatedSymbol.Name; diff --git a/Tests/CSharp/MemberTests/PropertyMemberTests.cs b/Tests/CSharp/MemberTests/PropertyMemberTests.cs index 790859d75..8202496b6 100644 --- a/Tests/CSharp/MemberTests/PropertyMemberTests.cs +++ b/Tests/CSharp/MemberTests/PropertyMemberTests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using ICSharpCode.CodeConverter.Tests.TestRunners; using Xunit; @@ -874,4 +874,49 @@ public static IEnumerable SomeObjects } }"); } -} \ No newline at end of file + /// Issue #827: VB auto-property backing field access (_Prop) should map to MyClassProp for overridable properties + [Fact] + public async Task TestOverridableAutoPropertyBackingFieldAccessAsync() + { + await TestConversionVisualBasicToCSharpAsync(@"Class Foo + Overridable Property Prop As Integer = 5 + + Sub Test() + _Prop = 10 + Dim isCorrect = MyClass.Prop = 10 + End Sub +End Class +Class Child + Inherits Foo + Overrides Property Prop As Integer = 20 +End Class", @" +internal partial class Foo +{ + public int MyClassProp { get; set; } = 5; + + public virtual int Prop + { + get + { + return MyClassProp; + } + + set + { + MyClassProp = value; + } + } + + public void Test() + { + MyClassProp = 10; + bool isCorrect = MyClassProp == 10; + } +} + +internal partial class Child : Foo +{ + public override int Prop { get; set; } = 20; +}"); + } +} diff --git a/Tests/VB/MemberTests.cs b/Tests/VB/MemberTests.cs index 7aab41005..570e5cd80 100644 --- a/Tests/VB/MemberTests.cs +++ b/Tests/VB/MemberTests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using ICSharpCode.CodeConverter.Tests.TestRunners; using ICSharpCode.CodeConverter.VB; using Xunit; @@ -1495,49 +1495,4 @@ End Sub End Class"); } - - [Fact] - public async Task TestMyClassPropertyAccess() - { - await TestConversionVisualBasicToCSharpAsync(@" -Class Foo - Overridable Property Prop As Integer = 5 - - Sub Test() - _Prop = 10 ' This should convert to MyClassProp = 10 not to Prop = 10 - Dim isCorrect = MyClass.Prop = 10 ' After conversion this will return 5instead of 10 because we wrote to Child.Prop - End Sub -End Class -Class Child - Inherits Foo - Overrides Property Prop As Integer = 20 -End Class", @" -internal partial class Foo -{ - public int MyClassProp { get; set; } = 5; - - public virtual int Prop - { - get - { - return MyClassProp; - } - - set - { - MyClassProp = value; - } - } - - public void Test() - { - MyClassProp = 10; // This should convert to MyClassProp = 10 not to Prop = 10 - bool isCorrect = MyClassProp == 10; // After conversion this will return 5instead of 10 because we wrote to Child.Prop - } -} -internal partial class Child : Foo -{ - public override int Prop { get; set; } = 20; -}"); - } } From a68e8876e5baacaff4aa5abeabda2334c6169c35 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 22:06:07 +0000 Subject: [PATCH 5/7] Fix compilation error: remove extra _semanticModel argument from GetMyClassAccessedNames call The method was changed to instance method using _semanticModel directly, but the call site still passed _semanticModel as an argument causing a compilation error. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- CodeConverter/CSharp/DeclarationNodeVisitor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeConverter/CSharp/DeclarationNodeVisitor.cs b/CodeConverter/CSharp/DeclarationNodeVisitor.cs index a999cac40..c7d3e72a8 100644 --- a/CodeConverter/CSharp/DeclarationNodeVisitor.cs +++ b/CodeConverter/CSharp/DeclarationNodeVisitor.cs @@ -336,7 +336,7 @@ var dummyClass public override async Task VisitClassBlock(VBSyntax.ClassBlockSyntax node) { - _accessorDeclarationNodeConverter.AccessedThroughMyClass = GetMyClassAccessedNames(node, _semanticModel); + _accessorDeclarationNodeConverter.AccessedThroughMyClass = GetMyClassAccessedNames(node); var classStatement = node.ClassStatement; var attributes = await CommonConversions.ConvertAttributesAsync(classStatement.AttributeLists); var (parameters, constraints) = await SplitTypeParametersAsync(classStatement.TypeParameterList); From dc0596b5598011d4a8afd1e1d2858b2b0c280062 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 22:51:40 +0000 Subject: [PATCH 6/7] Remove issue-557 char-default tests that depend on a fix not in this branch Tests TestCharDefaultValueForStringParameterAsync and TestCharLiteralDefaultValueForStringParameterAsync require the FixCharDefaultsForStringParams fix which is in a separate PR. Removing them from the issue-827 branch to prevent test failures. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- Tests/CSharp/MemberTests/MemberTests.cs | 45 ------------------------- 1 file changed, 45 deletions(-) diff --git a/Tests/CSharp/MemberTests/MemberTests.cs b/Tests/CSharp/MemberTests/MemberTests.cs index 8507d5086..da604f2dc 100644 --- a/Tests/CSharp/MemberTests/MemberTests.cs +++ b/Tests/CSharp/MemberTests/MemberTests.cs @@ -1576,49 +1576,4 @@ private void OptionalByRefWithDefault([Optional][DefaultParameterValue(""a"")] r CS7036: There is no argument given that corresponds to the required parameter 'str1' of 'MissingByRefArgumentWithNoExplicitDefaultValue.ByRefNoDefault(ref string)' "); } - - [Fact] - public async Task TestCharDefaultValueForStringParameterAsync() - { - // Issue #557: VB allows Char as a default value for a String parameter, but C# does not. - // The fix replaces the default with null and prepends a null-coalescing assignment in the body. - await TestConversionVisualBasicToCSharpAsync( - @"Module TestModule - Friend Const DlM As Char = ""^""c - - Friend Function LeftSideOf(Optional ByVal strDlM As String = DlM) As String - Return strDlM - End Function -End Module", @" -internal static partial class TestModule -{ - internal const char DlM = '^'; - - internal static string LeftSideOf(string strDlM = null) - { - strDlM = strDlM ?? DlM.ToString(); - return strDlM; - } -}"); - } - - [Fact] - public async Task TestCharLiteralDefaultValueForStringParameterAsync() - { - // Issue #557: inline char literal as default value for a String parameter. - await TestConversionVisualBasicToCSharpAsync( - @"Class TestClass - Friend Function Foo(Optional s As String = ""^""c) As String - Return s - End Function -End Class", @" -internal partial class TestClass -{ - internal string Foo(string s = null) - { - s = s ?? '^'.ToString(); - return s; - } -}"); - } } \ No newline at end of file From bdca73c2d468bce69459851f698b8038c8a5912f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Apr 2026 23:30:55 +0000 Subject: [PATCH 7/7] Fix double MyClass prefix when converting MyClass._Prop backing field access When _Prop is accessed via MyClass._Prop, NameExpressionNodeVisitor.cs already adds the MyClass prefix via "$MyClass{ConvertIdentifier(...).ValueText}". The new ConvertIdentifier code was also returning MyClassProp for _Prop, causing a double prefix MyClassMyClassProp. Fix: detect when _Prop is part of a MyClass._Prop member access and return the bare property name so NameExpressionNodeVisitor can prefix it. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- CodeConverter/CSharp/CommonConversions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs index c30e2edce..8b24d499d 100644 --- a/CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -293,8 +293,10 @@ public SyntaxToken ConvertIdentifier(SyntaxToken id, bool isAttribute = false, S // AND the first explicitly declared parameter is this symbol, we need to replace it with value. text = "value"; } else if (normalizedText.StartsWith("_", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol propertyFieldSymbol && propertyFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Property) == true) { - // For virtual auto-properties, VB backing field _Prop maps to the C# MyClassProp backing property (bypasses virtual dispatch) - text = propertyFieldSymbol.IsImplicitlyDeclared && propertyFieldSymbol.AssociatedSymbol is IPropertySymbol { IsVirtual: true, IsAbstract: false } vProp + // For virtual auto-properties, VB backing field _Prop maps to the C# MyClassProp backing property (bypasses virtual dispatch). + // Exception: when accessed as MyClass._Prop, NameExpressionNodeVisitor adds the "MyClass" prefix itself, so we just return the property name. + var isAccessedViaMyClass = id.Parent?.Parent is VBSyntax.MemberAccessExpressionSyntax { Expression: VBSyntax.MyClassExpressionSyntax }; + text = !isAccessedViaMyClass && propertyFieldSymbol.IsImplicitlyDeclared && propertyFieldSymbol.AssociatedSymbol is IPropertySymbol { IsVirtual: true, IsAbstract: false } vProp ? "MyClass" + vProp.Name : propertyFieldSymbol.AssociatedSymbol.Name; } else if (normalizedText.EndsWith("Event", StringComparison.OrdinalIgnoreCase) && idSymbol is IFieldSymbol eventFieldSymbol && eventFieldSymbol.AssociatedSymbol?.IsKind(SymbolKind.Event) == true) {