Skip to content

Commit

Permalink
Handle MidAssignmentStatement
Browse files Browse the repository at this point in the history
Part of #544
  • Loading branch information
GrahamTheCoder committed May 3, 2020
1 parent d2d7e2e commit 34317c1
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* Fix logic issue when converting expression passed in byref within conditional expression - [#310](https://github.com/icsharpcode/CodeConverter/issues/310)
* Added constructors now only added to the relevant type - not other types in the same file
* Converted non-static field initializers moved to constructor - [#281](https://github.com/icsharpcode/CodeConverter/issues/281)
* Convert assignments using "Mid" built-in function


### C# -> VB

Expand Down
7 changes: 4 additions & 3 deletions CodeConverter/CSharp/ExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1483,9 +1483,10 @@ private enum RefConversion
private ISymbol GetInvocationSymbol(SyntaxNode invocation)
{
var symbol = invocation.TypeSwitch(
(VBasic.Syntax.InvocationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch<ISymbol>(),
(VBasic.Syntax.ObjectCreationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch<ISymbol>(),
(VBasic.Syntax.RaiseEventStatementSyntax e) => _semanticModel.GetSymbolInfo(e.Name).ExtractBestMatch<ISymbol>(),
(VBSyntax.InvocationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch<ISymbol>(),
(VBSyntax.ObjectCreationExpressionSyntax e) => _semanticModel.GetSymbolInfo(e).ExtractBestMatch<ISymbol>(),
(VBSyntax.RaiseEventStatementSyntax e) => _semanticModel.GetSymbolInfo(e.Name).ExtractBestMatch<ISymbol>(),
(VBSyntax.MidExpressionSyntax e) => _semanticModel.Compilation.GetTypeByMetadataName("Microsoft.VisualBasic.CompilerServices.StringType")?.GetMembers("MidStmtStr").FirstOrDefault(),
_ => { throw new NotSupportedException(); }
);
return symbol;
Expand Down
30 changes: 25 additions & 5 deletions CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ public override async Task<SyntaxList<StatementSyntax>> VisitExpressionStatement

public override async Task<SyntaxList<StatementSyntax>> VisitAssignmentStatement(VBSyntax.AssignmentStatementSyntax node)
{
if (node.IsKind(VBasic.SyntaxKind.MidAssignmentStatement) && node.Left is VBSyntax.MidExpressionSyntax mes) {
return await ConvertMidAssignment(node, mes);
}

var lhs = (ExpressionSyntax) await node.Left.AcceptAsync(_expressionVisitor);
var lOperation = _semanticModel.GetOperation(node.Left);

Expand All @@ -160,7 +164,7 @@ _methodNode is VBSyntax.MethodBlockSyntax mb &&

if (node.IsKind(VBasic.SyntaxKind.ExponentiateAssignmentStatement)) {
rhs = SyntaxFactory.InvocationExpression(
SyntaxFactory.ParseExpression($"{nameof(Math)}.{nameof(Math.Pow)}"),
ValidSyntaxFactory.MemberAccess(nameof(Math), nameof(Math.Pow)),
ExpressionSyntaxExtensions.CreateArgList(lhs, rhs));
}
var kind = node.Kind().ConvertToken(TokenContext.Local);
Expand All @@ -172,6 +176,21 @@ _methodNode is VBSyntax.MethodBlockSyntax mb &&
return postAssignment.Insert(0, SyntaxFactory.ExpressionStatement(assignment));
}

private async Task<SyntaxList<StatementSyntax>> ConvertMidAssignment(VBSyntax.AssignmentStatementSyntax node, VBSyntax.MidExpressionSyntax mes)
{
_extraUsingDirectives.Add("Microsoft.VisualBasic.CompilerServices");
var midFunction = ValidSyntaxFactory.MemberAccess("StringType", "MidStmtStr");
var midArgList = (ArgumentListSyntax)await mes.ArgumentList.AcceptAsync(_expressionVisitor);
var (reusable, statements, _) = await GetExpressionWithoutSideEffectsAsync(node.Right, "midTmp");
if (midArgList.Arguments.Count == 2) {
var length = ValidSyntaxFactory.MemberAccess(reusable, "Length");
midArgList = midArgList.AddArguments(SyntaxFactory.Argument(length));
}
midArgList = midArgList.AddArguments(SyntaxFactory.Argument(reusable));
var invokeMid = SyntaxFactory.InvocationExpression(midFunction, midArgList);
return statements.Add(SyntaxFactory.ExpressionStatement(invokeMid));
}

/// <remarks>
/// <see cref="CommonConversions.ConvertIdentifier"/> ensures we convert the property access to a field access
/// </remarks>
Expand Down Expand Up @@ -214,7 +233,7 @@ private async Task<SyntaxList<StatementSyntax>> ConvertRedimClause(VBSyntax.Redi
if (!preserve) return SingleStatement(newArrayAssignment);

var lastIdentifierText = node.Expression.DescendantNodesAndSelf().OfType<VBSyntax.IdentifierNameSyntax>().Last().Identifier.Text;
var (oldTargetExpression, stmts, _) = await GetExpressionWithoutSideEffects(node.Expression, "old" + lastIdentifierText.ToPascalCase(), true);
var (oldTargetExpression, stmts, _) = await GetExpressionWithoutSideEffectsAsync(node.Expression, "old" + lastIdentifierText.ToPascalCase(), true);
var arrayCopyIfNotNull = CreateConditionalArrayCopy(node, (IdentifierNameSyntax) oldTargetExpression, csTargetArrayExpression, convertedBounds);

return stmts.AddRange(new StatementSyntax[] {newArrayAssignment, arrayCopyIfNotNull});
Expand Down Expand Up @@ -618,7 +637,7 @@ public override async Task<SyntaxList<StatementSyntax>> VisitGoToStatement(VBSyn
public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSyntax.SelectBlockSyntax node)
{
var vbExpr = node.SelectStatement.Expression;
var (csExpr, stmts, csExprWithSourceMapping) = await GetExpressionWithoutSideEffects(vbExpr, "switchExpr");
var (csExpr, stmts, csExprWithSourceMapping) = await GetExpressionWithoutSideEffectsAsync(vbExpr, "switchExpr");
var usedConstantValues = new HashSet<object>();
var sections = new List<SwitchSectionSyntax>();
foreach (var block in node.CaseBlocks) {
Expand Down Expand Up @@ -666,9 +685,10 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta
return stmts.Add(switchStatementSyntax);
}

private async Task<(ExpressionSyntax Reusable, SyntaxList<StatementSyntax> Statements, ExpressionSyntax SingleUse)> GetExpressionWithoutSideEffects(VBSyntax.ExpressionSyntax vbExpr, string variableNameBase, bool forceVariable = false)
private async Task<(ExpressionSyntax Reusable, SyntaxList<StatementSyntax> Statements, ExpressionSyntax SingleUse)> GetExpressionWithoutSideEffectsAsync(VBSyntax.ExpressionSyntax vbExpr, string variableNameBase, bool forceVariable = false)
{
var expr = (ExpressionSyntax)await vbExpr.AcceptAsync(_expressionVisitor);
expr = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(vbExpr, expr);
SyntaxList<StatementSyntax> stmts = SyntaxFactory.List<StatementSyntax>();
ExpressionSyntax exprWithoutSideEffects;
ExpressionSyntax reusableExprWithoutSideEffects;
Expand Down Expand Up @@ -723,7 +743,7 @@ private CasePatternSwitchLabelSyntax WrapInCasePatternSwitchLabelSyntax(VBSyntax

public override async Task<SyntaxList<StatementSyntax>> VisitWithBlock(VBSyntax.WithBlockSyntax node)
{
var (lhsExpression, prefixDeclarations, _) = await GetExpressionWithoutSideEffects(node.WithStatement.Expression, "withBlock");
var (lhsExpression, prefixDeclarations, _) = await GetExpressionWithoutSideEffectsAsync(node.WithStatement.Expression, "withBlock");

_withBlockLhs.Push(lhsExpression);
try {
Expand Down
5 changes: 3 additions & 2 deletions CodeConverter/CSharp/ValidSyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ public static SwitchStatementSyntax SwitchStatement(ExpressionSyntax expr, List<
SyntaxFactory.List(sections), SyntaxFactory.Token(SyntaxKind.CloseBraceToken));
}

public static ExpressionSyntax MemberAccess(params string[] nameParts)
public static ExpressionSyntax MemberAccess(params string[] nameParts) => MemberAccess(null, nameParts);

public static ExpressionSyntax MemberAccess(ExpressionSyntax lhs, params string[] nameParts)
{
ExpressionSyntax lhs = null;
foreach (var namePart in nameParts) {
if (lhs == null) lhs = SyntaxFactory.IdentifierName(namePart);
else {
Expand Down
38 changes: 38 additions & 0 deletions Tests/CSharp/SpecialConversionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,44 @@ public partial class Issue483
public int Test4 = 0x7D;
public int Test5 = 0x7E;
public int Test6 = 0x7F;
}");
}

[Fact]
public async Task Issue544_AssignUsingMid()
{
await TestConversionVisualBasicToCSharpAsync(
@"Public Class Issue483
Private Function numstr(ByVal aDouble As Double) As String
Dim str_Txt As String = Format(aDouble, ""0.000000"")
Mid(str_Txt, Len(str_Txt) - 6, 1) = "".""
Mid(str_Txt, Len(str_Txt) - 6) = "".""
Mid(str_Txt, Len(str_Txt) - 6) = aDouble
Console.WriteLine(aDouble)
If aDouble > 5.0 Then Mid(str_Txt, Len(str_Txt) - 6) = numstr(aDouble - 1.0)
Return str_Txt
End Function
End Class", @"using System;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
public partial class Issue483
{
private string numstr(double aDouble)
{
string str_Txt = Strings.Format(aDouble, ""0.000000"");
StringType.MidStmtStr(ref str_Txt, Strings.Len(str_Txt) - 6, 1, ""."");
StringType.MidStmtStr(ref str_Txt, Strings.Len(str_Txt) - 6, ""."".Length, ""."");
StringType.MidStmtStr(ref str_Txt, Strings.Len(str_Txt) - 6, aDouble.ToString().Length, aDouble.ToString());
Console.WriteLine(aDouble);
if (aDouble > 5.0)
{
var midTmp = numstr(aDouble - 1.0);
StringType.MidStmtStr(ref str_Txt, Strings.Len(str_Txt) - 6, midTmp.Length, midTmp);
}
return str_Txt;
}
}");
}
}
Expand Down

0 comments on commit 34317c1

Please sign in to comment.