Skip to content

Commit c118125

Browse files
committed
Fix issue #886: use default instead of null for optional ByRef struct params
When a VB method has an optional ByRef struct (value type) parameter with Nothing as the default, the converter previously generated `null` as the initializer for the hoisted local variable. `null` is not valid for value types in C#, causing a compilation error. The fix introduces a `LiteralOrDefault` helper that returns the `default` literal when the parameter type is a value type and the explicit default value is null. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX
1 parent 2bb1568 commit c118125

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

CodeConverter/CSharp/ArgumentConverter.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ CSSyntax.ArgumentSyntax ConvertOmittedArgument(IParameterSymbol parameter)
9797
var csRefKind = CommonConversions.GetCsRefKind(parameter);
9898
return csRefKind != RefKind.None
9999
? CreateOptionalRefArg(parameter, csRefKind)
100-
: CS.SyntaxFactory.Argument(CommonConversions.Literal(parameter.ExplicitDefaultValue));
100+
: CS.SyntaxFactory.Argument(LiteralOrDefault(parameter.ExplicitDefaultValue, parameter.Type));
101101
}
102102
}
103103

@@ -144,6 +144,14 @@ internal CSSyntax.ExpressionSyntax HoistByRefDeclaration(VBSyntax.ExpressionSynt
144144
return local.IdentifierName;
145145
}
146146

147+
private static CSSyntax.ExpressionSyntax LiteralOrDefault(object value, ITypeSymbol paramType)
148+
{
149+
if (value is null && paramType.IsValueType) {
150+
return ValidSyntaxFactory.DefaultExpression;
151+
}
152+
return CommonConversions.Literal(value);
153+
}
154+
147155
private ISymbol GetInvocationSymbol(SyntaxNode invocation)
148156
{
149157
var symbol = invocation.TypeSwitch(
@@ -226,7 +234,7 @@ private CSSyntax.ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p, RefKind
226234
var type = CommonConversions.GetTypeSyntax(p.Type);
227235
CSSyntax.ExpressionSyntax initializer;
228236
if (p.HasExplicitDefaultValue) {
229-
initializer = CommonConversions.Literal(p.ExplicitDefaultValue);
237+
initializer = LiteralOrDefault(p.ExplicitDefaultValue, p.Type);
230238
} else if (HasOptionalAttribute(p)) {
231239
if (TryGetDefaultParameterValueAttributeValue(p, out var defaultValue)) {
232240
initializer = CommonConversions.Literal(defaultValue);

Tests/CSharp/ExpressionTests/ByRefTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,4 +961,34 @@ End Sub
961961
Assert.Contains("GetLicenseMaybe", output);
962962
}
963963

964+
[Fact]
965+
public async Task OptionalStructRefParameterUsesDefaultNotNullIssue886Async()
966+
{
967+
// Issue #886: When a VB method has an optional ByRef struct parameter with Nothing as the default,
968+
// the generated C# should use `default` rather than `null` (null is not valid for value types).
969+
await TestConversionVisualBasicToCSharpAsync(@"
970+
Structure S
971+
End Structure
972+
Sub Foo(Optional ByRef s As S = Nothing)
973+
End Sub
974+
Sub Bar()
975+
Foo()
976+
End Sub
977+
", @"using System.Runtime.InteropServices;
978+
979+
internal partial struct S
980+
{
981+
}
982+
983+
public void Foo([Optional] ref S s)
984+
{
985+
}
986+
987+
public void Bar()
988+
{
989+
S args = default;
990+
Foo(s: ref args);
991+
}");
992+
}
993+
964994
}

0 commit comments

Comments
 (0)