Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,22 @@ public override async Task<SyntaxList<StatementSyntax>> VisitAssignmentStatement
var lhs = await node.Left.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
var lOperation = _semanticModel.GetOperation(node.Left);

//Already dealt with by call to the same method in ConvertInvocationExpression
var (parameterizedPropertyAccessMethod, _) = await CommonConversions.GetParameterizedPropertyAccessMethodAsync(lOperation);
if (parameterizedPropertyAccessMethod != null) return SingleStatement(lhs);

// If it's a simple assignment, we can return early as it's already handled by ConvertInvocationExpression
if (parameterizedPropertyAccessMethod != null && node.IsKind(VBasic.SyntaxKind.SimpleAssignmentStatement)) {
return SingleStatement(lhs);
}

// For compound assignments, we want to expand it to the setter, but parameterizedPropertyAccessMethod above
// returned 'get_Item' or 'set_Item' depending on operation context.
// We know for sure the left-hand side is a getter invocation for compound assignments (e.g. this.get_Item(0) += 2),
// but we need the setter name to build the final expression.
string setMethodName = null;
if (lOperation is IPropertyReferenceOperation pro && pro.Arguments.Any() && !Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions.IsDefault(pro.Property)) {
setMethodName = pro.Property.SetMethod?.Name;
}

var rhs = await node.Right.AcceptAsync<ExpressionSyntax>(_expressionVisitor);

if (node.Left is VBSyntax.IdentifierNameSyntax id &&
Expand Down Expand Up @@ -241,16 +254,41 @@ _methodNode is VBSyntax.MethodBlockSyntax mb &&

var nonCompoundRhs = SyntaxFactory.BinaryExpression(nonCompound, lhs, typeConvertedRhs);
var typeConvertedNonCompoundRhs = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Right, nonCompoundRhs, forceSourceType: rhsTypeInfo.ConvertedType, forceTargetType: lhsTypeInfo.Type);
if (nonCompoundRhs != typeConvertedNonCompoundRhs) {
if (nonCompoundRhs != typeConvertedNonCompoundRhs || setMethodName != null) {
kind = SyntaxKind.SimpleAssignmentExpression;
typeConvertedRhs = typeConvertedNonCompoundRhs;
}
} else if (setMethodName != null && node.IsKind(VBasic.SyntaxKind.ExponentiateAssignmentStatement)) {
// ExponentiateAssignmentStatement evaluates to Math.Pow invocation which might need casting back to lhsType
var typeConvertedNonCompoundRhs = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Right, typeConvertedRhs, forceSourceType: _semanticModel.Compilation.GetTypeByMetadataName("System.Double"), forceTargetType: lhsTypeInfo.Type);
kind = SyntaxKind.SimpleAssignmentExpression;
typeConvertedRhs = typeConvertedNonCompoundRhs;
}

rhs = typeConvertedRhs;

var assignment = SyntaxFactory.AssignmentExpression(kind, lhs, rhs);

if (setMethodName != null) {
if (lhs is InvocationExpressionSyntax ies) {
ExpressionSyntax exprToReplace = ies.Expression;
if (exprToReplace is MemberAccessExpressionSyntax maes) {
var newName = SyntaxFactory.IdentifierName(setMethodName).WithTriviaFrom(maes.Name);
var stripThis = maes.Expression is ThisExpressionSyntax
&& node.Left.SkipIntoParens() is not VBSyntax.MemberAccessExpressionSyntax {
Expression: not (VBSyntax.MeExpressionSyntax or VBSyntax.MyClassExpressionSyntax)
};
exprToReplace = stripThis ? newName.WithTriviaFrom(maes) : maes.WithName(newName);
} else if (exprToReplace is IdentifierNameSyntax) {
exprToReplace = SyntaxFactory.IdentifierName(setMethodName).WithTriviaFrom(exprToReplace);
}
var newArgList = ies.ArgumentList.AddArguments(SyntaxFactory.Argument(rhs));
var newLhs = ies.WithExpression(exprToReplace).WithArgumentList(newArgList);
var postAssign = GetPostAssignmentStatements(node);
return postAssign.Insert(0, SyntaxFactory.ExpressionStatement(newLhs));
}
return SingleStatement(lhs);
}

var assignment = SyntaxFactory.AssignmentExpression(kind, lhs, rhs);
var postAssignment = GetPostAssignmentStatements(node);
return postAssignment.Insert(0, SyntaxFactory.ExpressionStatement(assignment));
}
Expand Down
75 changes: 75 additions & 0 deletions Tests/CSharp/StatementTests/MethodStatementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1713,4 +1713,79 @@ public void TestMethod()
}
}");
}


[Fact]
public async Task AssignmentOperatorsParameterizedPropertiesAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"Public Class TestClass
Private _items As Integer() = New Integer() {1}
Public Property Item(index As Integer) As Integer
Get
Return _items(index)
End Get
Set(value As Integer)
_items(index) = value
End Set
End Property

Private _strItems As String() = New String() {""Hello""}
Public Property StrItem(index As Integer) As String
Get
Return _strItems(index)
End Get
Set(value As String)
_strItems(index) = value
End Set
End Property

Public Sub AllAssignmentOperators()
Item(0) += 2
Item(0) *= 2
Item(0) ^= 2
Item(0) /= 2
Item(0) -= 2
Item(0) \= 2
Item(0) <<= 2
Item(0) >>= 2
StrItem(0) &= "" World""
End Sub
End Class", @"using System;

public partial class TestClass
{
private int[] _items = new int[] { 1 };
public int get_Item(int index)
{
return _items[index];
}
public void set_Item(int index, int value)
{
_items[index] = value;
}

private string[] _strItems = new string[] { ""Hello"" };
public string get_StrItem(int index)
{
return _strItems[index];
}
public void set_StrItem(int index, string value)
{
_strItems[index] = value;
}

public void AllAssignmentOperators()
{
set_Item(0, get_Item(0) + 2);
set_Item(0, get_Item(0) * 2);
set_Item(0, (int)Math.Round(Math.Pow(get_Item(0), 2d)));
set_Item(0, (int)Math.Round(get_Item(0) / 2d));
set_Item(0, get_Item(0) - 2);
set_Item(0, get_Item(0) / 2);
set_Item(0, get_Item(0) << 2);
set_Item(0, get_Item(0) >> 2);
set_StrItem(0, get_StrItem(0) + "" World"");
}
}");
}
}
Loading