diff --git a/CodeConverter/CSharp/ArgumentConverter.cs b/CodeConverter/CSharp/ArgumentConverter.cs index 083801d9..ffd47cb6 100644 --- a/CodeConverter/CSharp/ArgumentConverter.cs +++ b/CodeConverter/CSharp/ArgumentConverter.cs @@ -226,10 +226,10 @@ 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); + initializer = LiteralOrDefault(defaultValue, p.Type); } else { initializer = CS.SyntaxFactory.DefaultExpression(type); } @@ -273,6 +273,18 @@ bool TryGetDefaultParameterValueAttributeValue(IParameterSymbol p, out object de } } + /// + /// Returns a literal expression for the given value, or a default expression when the value is null + /// and the parameter type is a value type (e.g. a struct), since null is not a valid C# initializer for value types. + /// + private static CSSyntax.ExpressionSyntax LiteralOrDefault(object value, ITypeSymbol paramType) + { + if (value is null && paramType.IsValueType) { + return ValidSyntaxFactory.DefaultExpression; + } + return CommonConversions.Literal(value); + } + public CSSyntax.ArgumentListSyntax CreateArgList(ISymbol invocationSymbol) { return CS.SyntaxFactory.ArgumentList(CS.SyntaxFactory.SeparatedList( diff --git a/Tests/CSharp/ExpressionTests/ByRefTests.cs b/Tests/CSharp/ExpressionTests/ByRefTests.cs index a5badfd3..722e12c6 100644 --- a/Tests/CSharp/ExpressionTests/ByRefTests.cs +++ b/Tests/CSharp/ExpressionTests/ByRefTests.cs @@ -914,4 +914,41 @@ public static void LogAndReset(ref int arg) }"); } + [Fact] + public async Task OptionalStructRefParameterUsesDefaultNotNullIssue886Async() + { + await TestConversionVisualBasicToCSharpAsync(@"Public Class Issue886 + Private Shared Sub OptionalParams() + FunctionWithOptionalParams() + End Sub + + Private Shared Sub FunctionWithOptionalParams(Optional ByRef structParam As TestStruct = Nothing) + structParam = New TestStruct + End Sub + + Friend Structure TestStruct + Friend A As Boolean + End Structure +End Class", @"using System.Runtime.InteropServices; + +public partial class Issue886 +{ + private static void OptionalParams() + { + TestStruct argstructParam = default; + FunctionWithOptionalParams(structParam: ref argstructParam); + } + + private static void FunctionWithOptionalParams([Optional] ref TestStruct structParam) + { + structParam = new TestStruct(); + } + + internal struct TestStruct + { + internal bool A; + } +}"); + } + } \ No newline at end of file