diff --git a/CodeConverter/CSharp/ArgumentConverter.cs b/CodeConverter/CSharp/ArgumentConverter.cs index 083801d9..6eec58ac 100644 --- a/CodeConverter/CSharp/ArgumentConverter.cs +++ b/CodeConverter/CSharp/ArgumentConverter.cs @@ -97,7 +97,7 @@ CSSyntax.ArgumentSyntax ConvertOmittedArgument(IParameterSymbol parameter) var csRefKind = CommonConversions.GetCsRefKind(parameter); return csRefKind != RefKind.None ? CreateOptionalRefArg(parameter, csRefKind) - : CS.SyntaxFactory.Argument(CommonConversions.Literal(parameter.ExplicitDefaultValue)); + : CS.SyntaxFactory.Argument(LiteralOrDefault(parameter.ExplicitDefaultValue, parameter.Type)); } } @@ -144,6 +144,14 @@ internal CSSyntax.ExpressionSyntax HoistByRefDeclaration(VBSyntax.ExpressionSynt return local.IdentifierName; } + private static CSSyntax.ExpressionSyntax LiteralOrDefault(object value, ITypeSymbol paramType) + { + if (value is null && paramType.IsValueType) { + return ValidSyntaxFactory.DefaultExpression; + } + return CommonConversions.Literal(value); + } + private ISymbol GetInvocationSymbol(SyntaxNode invocation) { var symbol = invocation.TypeSwitch( @@ -226,7 +234,7 @@ private CSSyntax.ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p, RefKind var type = CommonConversions.GetTypeSyntax(p.Type); CSSyntax.ExpressionSyntax initializer; if (p.HasExplicitDefaultValue) { - initializer = CommonConversions.Literal(p.ExplicitDefaultValue); + initializer = LiteralOrDefault(p.ExplicitDefaultValue, p.Type); } else if (HasOptionalAttribute(p)) { if (TryGetDefaultParameterValueAttributeValue(p, out var defaultValue)) { initializer = CommonConversions.Literal(defaultValue); diff --git a/Tests/CSharp/ExpressionTests/ByRefTests.cs b/Tests/CSharp/ExpressionTests/ByRefTests.cs index 2d0be02d..4201ebc3 100644 --- a/Tests/CSharp/ExpressionTests/ByRefTests.cs +++ b/Tests/CSharp/ExpressionTests/ByRefTests.cs @@ -961,4 +961,34 @@ End Sub Assert.Contains("GetLicenseMaybe", output); } + [Fact] + public async Task OptionalStructRefParameterUsesDefaultNotNullIssue886Async() + { + // Issue #886: When a VB method has an optional ByRef struct parameter with Nothing as the default, + // the generated C# should use `default` rather than `null` (null is not valid for value types). + await TestConversionVisualBasicToCSharpAsync(@" +Structure S +End Structure +Sub Foo(Optional ByRef s As S = Nothing) +End Sub +Sub Bar() + Foo() +End Sub +", @"using System.Runtime.InteropServices; + +internal partial struct S +{ +} + +public void Foo([Optional] ref S s) +{ +} + +public void Bar() +{ + S args = default; + Foo(s: ref args); +}"); + } + } \ No newline at end of file