Skip to content

Commit 9e929e1

Browse files
Merge pull request #1126 from TymurGubayev/fix/HoistedOutParameterLambdaUsingByRefParameter/1
Pass ByRef parameters of parent into hoisted function by ref
2 parents c3a91c8 + 0294eaf commit 9e929e1

6 files changed

Lines changed: 106 additions & 11 deletions

File tree

CodeConverter/CSharp/CommonConversions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public bool ShouldPreferExplicitType(VBSyntax.ExpressionSyntax exp,
145145
equalsValueClauseSyntax = null;
146146
} else {
147147
var returnBlock = SyntaxFactory.Block(SyntaxFactory.ReturnStatement(adjustedInitializerExpr));
148-
_typeContext.PerScopeState.Hoist(new HoistedParameterlessFunction(GetInitialValueFunctionName(vbName), csTypeSyntax, returnBlock));
148+
_typeContext.PerScopeState.Hoist(new HoistedFunction(GetInitialValueFunctionName(vbName), csTypeSyntax, returnBlock, null));
149149
equalsValueClauseSyntax = null;
150150
}
151151
} else {

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,9 +1174,41 @@ private async Task<InvocationExpressionSyntax> HoistAndCallLocalFunctionAsync(VB
11741174
statements.Concat(SyntaxFactory.ReturnStatement(ValidSyntaxFactory.IdentifierName(retVariableName)).Yield())
11751175
);
11761176
var returnType = CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType);
1177-
1178-
var localFunc = _typeContext.PerScopeState.Hoist(new HoistedParameterlessFunction(localFuncName, returnType, block));
1179-
return SyntaxFactory.InvocationExpression(localFunc.TempIdentifier, SyntaxFactory.ArgumentList());
1177+
1178+
//any argument that's a ByRef parameter of the parent method needs to be passed as a ref parameter to the local function (to avoid error CS1628)
1179+
var refParametersOfParent = GetRefParameters(invocation.ArgumentList);
1180+
var (args, @params) = CreateArgumentsAndParametersLists(refParametersOfParent);
1181+
1182+
var localFunc = _typeContext.PerScopeState.Hoist(new HoistedFunction(localFuncName, returnType, block, SyntaxFactory.ParameterList(@params)));
1183+
return SyntaxFactory.InvocationExpression(localFunc.TempIdentifier, SyntaxFactory.ArgumentList(args));
1184+
1185+
List<IParameterSymbol> GetRefParameters(VBSyntax.ArgumentListSyntax argumentList)
1186+
{
1187+
var result = new List<IParameterSymbol>();
1188+
if (argumentList is null) return result;
1189+
1190+
foreach (var arg in argumentList.Arguments) {
1191+
if (_semanticModel.GetSymbolInfo(arg.GetExpression()).Symbol is not IParameterSymbol p) continue;
1192+
if (p.RefKind != RefKind.None) {
1193+
result.Add(p);
1194+
}
1195+
}
1196+
1197+
return result;
1198+
}
1199+
1200+
(SeparatedSyntaxList<ArgumentSyntax>, SeparatedSyntaxList<ParameterSyntax>) CreateArgumentsAndParametersLists(List<IParameterSymbol> parameterSymbols)
1201+
{
1202+
var arguments = new List<ArgumentSyntax>();
1203+
var parameters = new List<ParameterSyntax>();
1204+
foreach (var p in parameterSymbols) {
1205+
var arg = (ArgumentSyntax)CommonConversions.CsSyntaxGenerator.Argument(p.RefKind, SyntaxFactory.IdentifierName(p.Name));
1206+
arguments.Add(arg);
1207+
var par = (ParameterSyntax)CommonConversions.CsSyntaxGenerator.ParameterDeclaration(p);
1208+
parameters.Add(par);
1209+
}
1210+
return (SyntaxFactory.SeparatedList(arguments), SyntaxFactory.SeparatedList(parameters));
1211+
}
11801212
}
11811213

11821214
private bool RequiresLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol)

CodeConverter/CSharp/HoistedParameterlessFunction.cs renamed to CodeConverter/CSharp/HoistedFunction.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,25 @@
33

44
namespace ICSharpCode.CodeConverter.CSharp;
55

6-
internal class HoistedParameterlessFunction : IHoistedNode
6+
internal class HoistedFunction : IHoistedNode
77
{
88
private readonly TypeSyntax _returnType;
99
private readonly BlockSyntax _block;
10+
private readonly ParameterListSyntax _parameters;
1011

1112
public string Id { get; }
1213
public string Prefix { get; }
1314

14-
public HoistedParameterlessFunction(string localFuncName, TypeSyntax returnType, BlockSyntax block)
15+
public HoistedFunction(string localFuncName, TypeSyntax returnType, BlockSyntax block, ParameterListSyntax parameters)
1516
{
1617
Id = $"hs{Guid.NewGuid().ToString("N")}";
1718
Prefix = localFuncName;
1819
_returnType = returnType;
1920
_block = block;
21+
_parameters = parameters;
2022
}
2123

2224
public IdentifierNameSyntax TempIdentifier => ValidSyntaxFactory.IdentifierName(Id).WithAdditionalAnnotations(PerScopeState.AdditionalLocalAnnotation);
23-
public LocalFunctionStatementSyntax AsLocalFunction(string functionName) => SyntaxFactory.LocalFunctionStatement(_returnType, SyntaxFactory.Identifier(functionName)).WithBody(_block);
24-
public MethodDeclarationSyntax AsInstanceMethod(string functionName) => ValidSyntaxFactory.CreateParameterlessMethod(functionName, _returnType, _block);
25+
public LocalFunctionStatementSyntax AsLocalFunction(string functionName) => SyntaxFactory.LocalFunctionStatement(_returnType, SyntaxFactory.Identifier(functionName)).WithParameterList(_parameters).WithBody(_block);
26+
public MethodDeclarationSyntax AsInstanceMethod(string functionName) => ValidSyntaxFactory.CreateMethod(functionName, _returnType, _parameters, _block);
2527
}

CodeConverter/CSharp/PerScopeState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ private StatementSyntax[] GetPostStatements()
8686
.ToArray();
8787
}
8888

89-
public IReadOnlyCollection<HoistedParameterlessFunction> GetParameterlessFunctions()
89+
public IReadOnlyCollection<HoistedFunction> GetParameterlessFunctions()
9090
{
91-
return _hoistedNodesPerScope.Peek().OfType<HoistedParameterlessFunction>().ToArray();
91+
return _hoistedNodesPerScope.Peek().OfType<HoistedFunction>().ToArray();
9292
}
9393

9494
public IReadOnlyCollection<HoistedFieldFromVbStaticVariable> GetFields()

CodeConverter/CSharp/ValidSyntaxFactory.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,17 @@ expressionSyntax is IdentifierNameSyntax ||
7474
}
7575

7676
public static MethodDeclarationSyntax CreateParameterlessMethod(string newMethodName, TypeSyntax type, BlockSyntax body)
77+
{
78+
var parameterList = SyntaxFactory.ParameterList();
79+
return CreateMethod(newMethodName, type, parameterList, body);
80+
}
81+
82+
public static MethodDeclarationSyntax CreateMethod(string newMethodName, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax body)
7783
{
7884
var modifiers = SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
7985
var typeConstraints = SyntaxFactory.List<TypeParameterConstraintClauseSyntax>();
80-
var parameterList = SyntaxFactory.ParameterList();
86+
parameterList ??= SyntaxFactory.ParameterList();
87+
8188
var methodAttrs = SyntaxFactory.List<AttributeListSyntax>();
8289

8390
ArrowExpressionClauseSyntax arrowExpression = null;

Tests/CSharp/MemberTests/MemberTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,60 @@ public VisualBasicClass()
385385
}
386386
}");
387387
}
388+
389+
[Fact]
390+
public async Task TestHoistedOutParameterLambdaUsingByRefParameterAsync()
391+
{
392+
await TestConversionVisualBasicToCSharpAsync(
393+
@"Public Class SomeClass
394+
Sub S(Optional ByRef x As Integer = -1)
395+
Dim i As Integer = 0
396+
If F1(x, i) Then
397+
ElseIf F2(x, i) Then
398+
ElseIf F3(x, i) Then
399+
End If
400+
End Sub
401+
402+
Function F1(x As Integer, ByRef o As Object) As Boolean : End Function
403+
Function F2(ByRef x As Integer, ByRef o As Object) As Boolean : End Function
404+
Function F3(ByRef x As Object, ByRef o As Object) As Boolean : End Function
405+
End Class", @"using System.Runtime.InteropServices;
406+
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
407+
408+
public partial class SomeClass
409+
{
410+
public void S([Optional, DefaultParameterValue(-1)] ref int x)
411+
{
412+
int i = 0;
413+
bool localF1(ref int x) { object argo = i; var ret = F1(x, ref argo); i = Conversions.ToInteger(argo); return ret; }
414+
bool localF2(ref int x) { object argo1 = i; var ret = F2(ref x, ref argo1); i = Conversions.ToInteger(argo1); return ret; }
415+
bool localF3(ref int x) { object argx = x; object argo2 = i; var ret = F3(ref argx, ref argo2); x = Conversions.ToInteger(argx); i = Conversions.ToInteger(argo2); return ret; }
416+
417+
if (localF1(ref x))
418+
{
419+
}
420+
else if (localF2(ref x))
421+
{
422+
}
423+
else if (localF3(ref x))
424+
{
425+
}
426+
}
427+
428+
public bool F1(int x, ref object o)
429+
{
430+
return default;
431+
}
432+
public bool F2(ref int x, ref object o)
433+
{
434+
return default;
435+
}
436+
public bool F3(ref object x, ref object o)
437+
{
438+
return default;
439+
}
440+
}");
441+
}
388442

389443
[Fact]
390444
public async Task TestMethodWithReturnTypeAsync()

0 commit comments

Comments
 (0)