Skip to content

Commit d704de3

Browse files
Handle array element references
1 parent 83e8182 commit d704de3

File tree

3 files changed

+87
-23
lines changed

3 files changed

+87
-23
lines changed

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -894,8 +894,8 @@ private async Task<CSharpSyntaxNode> ConvertInvocation(VBSyntax.InvocationExpres
894894

895895
async Task<(ExpressionSyntax, bool isElementAccess)> ConvertInvocationSubExpression()
896896
{
897-
var isElementAccess = IsPropertyElementAccess(operation) ||
898-
IsArrayElementAccess(operation) ||
897+
var isElementAccess = operation.IsPropertyElementAccess() ||
898+
operation.IsArrayElementAccess() ||
899899
ProbablyNotAMethodCall(node, expressionSymbol, expressionReturnType);
900900

901901
var expr = await node.Expression.AcceptAsync(TriviaConvertingExpressionVisitor);
@@ -1411,19 +1411,38 @@ private RefConversion NeedsVariableForArgument(VBasic.Syntax.ArgumentSyntax node
14111411
{
14121412
if (refKind == RefKind.None) return RefConversion.Inline;
14131413
if (!(node is VBSyntax.SimpleArgumentSyntax sas)) return RefConversion.PreAssigment;
1414-
bool isIdentifier = sas.Expression is VBasic.Syntax.IdentifierNameSyntax;
1415-
bool isMemberAccess = sas.Expression is VBasic.Syntax.MemberAccessExpressionSyntax;
1414+
var expression = sas.Expression.SkipParens();
14161415

1417-
var symbolInfo = GetSymbolInfoInDocument<ISymbol>(sas.Expression);
1418-
bool isProperty = symbolInfo != null && symbolInfo.IsKind(SymbolKind.Property);
1419-
bool isUsing = symbolInfo?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax()?.Parent?.Parent?.IsKind(VBasic.SyntaxKind.UsingStatement) == true;
1416+
return GetRefConversion(expression);
14201417

1421-
var typeInfo = _semanticModel.GetTypeInfo(sas.Expression);
1422-
bool isTypeMismatch = typeInfo.Type == null || !typeInfo.Type.Equals(typeInfo.ConvertedType);
1418+
RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)
1419+
{
1420+
var symbolInfo = GetSymbolInfoInDocument<ISymbol>(expression);
1421+
if (symbolInfo.IsKind(SymbolKind.Property)) return RefConversion.PreAndPostAssignment;
1422+
1423+
var typeInfo = _semanticModel.GetTypeInfo(expression);
1424+
bool isTypeMismatch = typeInfo.Type == null || !typeInfo.Type.Equals(typeInfo.ConvertedType);
1425+
1426+
if (isTypeMismatch || DeclaredInUsing(symbolInfo)) return RefConversion.PreAssigment;
1427+
1428+
if (expression is VBasic.Syntax.IdentifierNameSyntax || expression is VBSyntax.MemberAccessExpressionSyntax ||
1429+
IsRefArrayAcces(expression)) {
1430+
return RefConversion.Inline;
1431+
}
1432+
1433+
return RefConversion.PreAssigment;
1434+
}
1435+
1436+
bool IsRefArrayAcces(VBSyntax.ExpressionSyntax expression)
1437+
{
1438+
if (!(expression is VBSyntax.InvocationExpressionSyntax ies)) return false;
1439+
return _semanticModel.GetOperation(ies).IsArrayElementAccess() && GetRefConversion(ies.Expression) == RefConversion.Inline;
1440+
}
1441+
}
14231442

1424-
if (isProperty) return RefConversion.PreAndPostAssignment;
1425-
if ((!isIdentifier && !isMemberAccess) || isTypeMismatch || isUsing) return RefConversion.PreAssigment;
1426-
return RefConversion.Inline;
1443+
private static bool DeclaredInUsing(ISymbol symbolInfo)
1444+
{
1445+
return symbolInfo?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax()?.Parent?.Parent?.IsKind(VBasic.SyntaxKind.UsingStatement) == true;
14271446
}
14281447

14291448
/// <summary>
@@ -1481,16 +1500,6 @@ private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode
14811500
return expr.ReplaceToken(idToken, SyntaxFactory.Identifier(overrideIdentifier).WithTriviaFrom(idToken).WithAdditionalAnnotations(idToken.GetAnnotations()));
14821501
}
14831502

1484-
private static bool IsPropertyElementAccess(IOperation operation)
1485-
{
1486-
return operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && VBasic.VisualBasicExtensions.IsDefault(pro.Property);
1487-
}
1488-
1489-
private static bool IsArrayElementAccess(IOperation operation)
1490-
{
1491-
return operation != null && operation.Kind == OperationKind.ArrayElementReference;
1492-
}
1493-
14941503
/// <summary>
14951504
/// Chances of having an unknown delegate stored as a field/local seem lower than having an unknown non-delegate type with an indexer stored.
14961505
/// So for a standalone identifier err on the side of assuming it's an indexer.

CodeConverter/CSharp/OperationExtensions.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
using ICSharpCode.CodeConverter.Util.FromRoslyn;
1+
using System.Linq;
2+
using ICSharpCode.CodeConverter.Util.FromRoslyn;
23
using Microsoft.CodeAnalysis;
34
using Microsoft.CodeAnalysis.Operations;
5+
using VBasic = Microsoft.CodeAnalysis.VisualBasic;
6+
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
7+
48

59
namespace ICSharpCode.CodeConverter.CSharp
610
{
@@ -32,5 +36,15 @@ public static IOperation SkipParens(this IOperation operation, bool skipImplicit
3236
}
3337
}
3438
}
39+
40+
public static bool IsPropertyElementAccess(this IOperation operation)
41+
{
42+
return operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && VBasic.VisualBasicExtensions.IsDefault(pro.Property);
43+
}
44+
45+
public static bool IsArrayElementAccess(this IOperation operation)
46+
{
47+
return operation != null && operation.Kind == OperationKind.ArrayElementReference;
48+
}
3549
}
3650
}

Tests/CSharp/ExpressionTests/ByRefTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,47 @@ public void UsesRef(bool someBool, int someInt)
420420
421421
Console.WriteLine(someInt);
422422
}
423+
}");
424+
}
425+
426+
[Fact]
427+
public async Task Issue567()
428+
{
429+
await TestConversionVisualBasicToCSharpAsync(@"Public Class Issue567
430+
Dim arr(,) As String
431+
Dim lst As List(Of String) = New List(Of String)({1.ToString(), 2.ToString(), 3.ToString()})
432+
433+
Sub DoSomething(ByRef str As String)
434+
str = ""test""
435+
End Sub
436+
437+
Sub Main()
438+
DoSomething(arr(2, 2))
439+
DoSomething(lst(1))
440+
Debug.Assert(arr(2, 2) = ""test"")
441+
End Sub
442+
443+
End Class", @"using System.Collections.Generic;
444+
using System.Diagnostics;
445+
446+
public partial class Issue567
447+
{
448+
private string[,] arr;
449+
private List<string> lst = new List<string>(new[] { 1.ToString(), 2.ToString(), 3.ToString() });
450+
451+
public void DoSomething(ref string str)
452+
{
453+
str = ""test"";
454+
}
455+
456+
public void Main()
457+
{
458+
DoSomething(ref arr[2, 2]);
459+
string argstr = lst[1];
460+
DoSomething(ref argstr);
461+
lst[1] = argstr;
462+
Debug.Assert((arr[2, 2] ?? """") == ""test"");
463+
}
423464
}");
424465
}
425466
}

0 commit comments

Comments
 (0)