From ab93df3457fc137e9a32f461bcbf718fe5763e78 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder <2490482+GrahamTheCoder@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:14:11 +0000 Subject: [PATCH] Fix issue 886: Optional ByRef parameters default value conversion Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- CodeConverter/CSharp/ArgumentConverter.cs | 12 +++++- CodeConverter/CSharp/ExpressionNodeVisitor.cs | 17 +++++++- Tests/CSharp/MemberTests/MemberTests.cs | 41 ++++++++++++++++++- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/CodeConverter/CSharp/ArgumentConverter.cs b/CodeConverter/CSharp/ArgumentConverter.cs index 083801d95..f520f5248 100644 --- a/CodeConverter/CSharp/ArgumentConverter.cs +++ b/CodeConverter/CSharp/ArgumentConverter.cs @@ -226,10 +226,18 @@ private CSSyntax.ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p, RefKind var type = CommonConversions.GetTypeSyntax(p.Type); CSSyntax.ExpressionSyntax initializer; if (p.HasExplicitDefaultValue) { - initializer = CommonConversions.Literal(p.ExplicitDefaultValue); + if (p.ExplicitDefaultValue == null && p.Type.IsValueType && p.Type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) { + initializer = CS.SyntaxFactory.DefaultExpression(type); + } else { + initializer = CommonConversions.Literal(p.ExplicitDefaultValue); + } } else if (HasOptionalAttribute(p)) { if (TryGetDefaultParameterValueAttributeValue(p, out var defaultValue)) { - initializer = CommonConversions.Literal(defaultValue); + if (defaultValue == null && p.Type.IsValueType && p.Type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) { + initializer = CS.SyntaxFactory.DefaultExpression(type); + } else { + initializer = CommonConversions.Literal(defaultValue); + } } else { initializer = CS.SyntaxFactory.DefaultExpression(type); } diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.cs index e95dc0a66..85da37980 100644 --- a/CodeConverter/CSharp/ExpressionNodeVisitor.cs +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.cs @@ -471,7 +471,22 @@ public override async Task VisitParameter(VBSyntax.ParameterSy CS.SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("Optional")), }; if (!node.Default.Value.IsKind(VBasic.SyntaxKind.NothingLiteralExpression)) { - optionalAttributes.Add(CS.SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("DefaultParameterValue"), arg)); + if (vbSymbol?.Type?.SpecialType == SpecialType.System_Decimal && defaultExpression is CSSyntax.LiteralExpressionSyntax literalExpr) { + var literalValue = literalExpr.Token.Value; + if (literalValue is decimal decimalVal) { + var isInteger = decimalVal == Math.Round(decimalVal); + var newLiteral = isInteger ? CommonConversions.Literal((int)decimalVal) : CommonConversions.Literal((double)decimalVal); + arg = CommonConversions.CreateAttributeArgumentList(CS.SyntaxFactory.AttributeArgument(newLiteral)); + } + } + + if (vbSymbol?.Type?.TypeKind == TypeKind.Struct && vbSymbol?.Type?.SpecialType == SpecialType.None) { + arg = null; + } + + if (arg != null) { + optionalAttributes.Add(CS.SyntaxFactory.Attribute(ValidSyntaxFactory.IdentifierName("DefaultParameterValue"), arg)); + } } attributes.Insert(0, CS.SyntaxFactory.AttributeList(CS.SyntaxFactory.SeparatedList(optionalAttributes))); diff --git a/Tests/CSharp/MemberTests/MemberTests.cs b/Tests/CSharp/MemberTests/MemberTests.cs index da604f2dc..2492f4607 100644 --- a/Tests/CSharp/MemberTests/MemberTests.cs +++ b/Tests/CSharp/MemberTests/MemberTests.cs @@ -1576,4 +1576,43 @@ 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)' "); } -} \ No newline at end of file + [Fact] + public async Task OptionalByRefParameterAsync886() + { + await TestConversionVisualBasicToCSharpAsync(@"Class Issue886 + Private Shared Sub OptionalParams() + FunctionWithOptionalParams() + End Sub + + Private Shared Sub FunctionWithOptionalParams(Optional ByRef structParam As TestStruct = Nothing, Optional ByRef decimalParam As Decimal = 0) + structParam = New TestStruct + decimalParam = 0 + End Sub + + Friend Structure TestStruct + Friend A As Boolean + End Structure +End Class", @"using System.Runtime.InteropServices; + +internal partial class Issue886 +{ + private static void OptionalParams() + { + TestStruct argstructParam = default; + decimal argdecimalParam = 0m; + FunctionWithOptionalParams(structParam: ref argstructParam, decimalParam: ref argdecimalParam); + } + + private static void FunctionWithOptionalParams([Optional] ref TestStruct structParam, [Optional, DefaultParameterValue(0)] ref decimal decimalParam) + { + structParam = new TestStruct(); + decimalParam = 0m; + } + + internal partial struct TestStruct + { + internal bool A; + } +}"); + } +}