Skip to content

Commit c0745d8

Browse files
Don't try to ref foreach, Me or Using identifiers - fixes #1052
1 parent de43c73 commit c0745d8

3 files changed

Lines changed: 99 additions & 5 deletions

File tree

CodeConverter/CSharp/CachedReflectedDelegates.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public static ISymbol GetAssociatedField(this IPropertySymbol declaredSymbol) =>
2525
public static SyntaxTree GetEmbeddedSyntaxTree(this Location loc) =>
2626
GetCachedReflectedPropertyDelegate(loc, "PossiblyEmbeddedOrMySourceTree", ref _possiblyEmbeddedOrMySourceTree);
2727
private static Func<Location, SyntaxTree> _possiblyEmbeddedOrMySourceTree;
28+
29+
public static bool GetIsUsing(this ILocalSymbol l) =>
30+
GetCachedReflectedPropertyDelegate(l, "IsUsing", ref _isUsing);
31+
private static Func<ILocalSymbol, bool> _isUsing;
32+
33+
2834

2935
/// <remarks>Unfortunately the roslyn UnassignedVariablesWalker and all useful collections created from it are internal only
3036
/// Other attempts using DataFlowsIn on each reference showed that "DataFlowsIn" even from an uninitialized variable (at least in the case of ints)

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,10 @@ private static CasePatternSwitchLabelSyntax VarWhen(SyntaxToken varName, Express
870870
SyntaxList<StatementSyntax> stmts = SyntaxFactory.List<StatementSyntax>();
871871
ExpressionSyntax exprWithoutSideEffects;
872872
ExpressionSyntax reusableExprWithoutSideEffects;
873-
if (!await CanEvaluateMultipleTimesAsync(vbExpr)) {
873+
if (IsReusableReadOnlyLocalKind(_semanticModel.GetSymbolInfo(vbExpr).Symbol) || await CanEvaluateMultipleTimesAsync(vbExpr)) {
874+
exprWithoutSideEffects = expr;
875+
reusableExprWithoutSideEffects = expr.WithoutSourceMapping();
876+
} else {
874877
TypeSyntax forceType = null;
875878
if (_semanticModel.GetOperation(vbExpr.SkipIntoParens()).IsAssignableExpression()) {
876879
forceType = SyntaxFactory.RefType(ValidSyntaxFactory.VarType);
@@ -880,14 +883,13 @@ private static CasePatternSwitchLabelSyntax VarWhen(SyntaxToken varName, Express
880883
var (stmt, id) = CreateLocalVariableWithUniqueName(vbExpr, variableNameBase, expr, forceType);
881884
stmts = stmts.Add(stmt);
882885
reusableExprWithoutSideEffects = exprWithoutSideEffects = id;
883-
} else {
884-
exprWithoutSideEffects = expr;
885-
reusableExprWithoutSideEffects = expr.WithoutSourceMapping();
886886
}
887887

888888
return (reusableExprWithoutSideEffects, stmts, exprWithoutSideEffects);
889889
}
890890

891+
private static bool IsReusableReadOnlyLocalKind(ISymbol symbol) => symbol is ILocalSymbol ls && (VBasic.VisualBasicExtensions.IsForEach(ls) || ls.GetIsUsing());
892+
891893
private (StatementSyntax Declaration, IdentifierNameSyntax Reference) CreateLocalVariableWithUniqueName(VBSyntax.ExpressionSyntax vbExpr, string variableNameBase, ExpressionSyntax expr, TypeSyntax forceType = null)
892894
{
893895
var contextNode = vbExpr.GetAncestor<VBSyntax.MethodBlockBaseSyntax>() ?? (VBasic.VisualBasicSyntaxNode) vbExpr.Parent;
@@ -903,7 +905,7 @@ private static CasePatternSwitchLabelSyntax VarWhen(SyntaxToken varName, Express
903905

904906
private async Task<bool> CanEvaluateMultipleTimesAsync(VBSyntax.ExpressionSyntax vbExpr)
905907
{
906-
return _semanticModel.GetConstantValue(vbExpr).HasValue || vbExpr.SkipIntoParens() is VBSyntax.NameSyntax ns && await IsNeverMutatedAsync(ns);
908+
return _semanticModel.GetConstantValue(vbExpr).HasValue || vbExpr.IsKind(VBasic.SyntaxKind.MeExpression) || vbExpr.SkipIntoParens() is VBSyntax.NameSyntax ns && await IsNeverMutatedAsync(ns);
907909
}
908910

909911
private async Task<bool> IsNeverMutatedAsync(VBSyntax.NameSyntax ns)

Tests/CSharp/StatementTests/MethodStatementTests.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,92 @@ public partial struct SomeStruct
530530
}");
531531
}
532532

533+
[Fact]
534+
public async Task WithBlockMeClassAsync()
535+
{
536+
await TestConversionVisualBasicToCSharpAsync(@"Public Class TestWithMe
537+
Private _x As Integer
538+
Sub S()
539+
With Me
540+
._x = 1
541+
._x = 2
542+
End With
543+
End Sub
544+
End Class", @"
545+
public partial class TestWithMe
546+
{
547+
private int _x;
548+
public void S()
549+
{
550+
_x = 1;
551+
_x = 2;
552+
}
553+
}");
554+
}
555+
556+
[Fact]
557+
public async Task WithBlockMeStructAsync()
558+
{
559+
await TestConversionVisualBasicToCSharpAsync(@"Public Structure TestWithMe
560+
Private _x As Integer
561+
Sub S()
562+
With Me
563+
._x = 1
564+
._x = 2
565+
End With
566+
End Sub
567+
End Structure", @"
568+
public partial struct TestWithMe
569+
{
570+
private int _x;
571+
public void S()
572+
{
573+
_x = 1;
574+
_x = 2;
575+
}
576+
}");
577+
}
578+
579+
[Fact]
580+
public async Task WithBlockForEachAsync()
581+
{
582+
await TestConversionVisualBasicToCSharpAsync(@"Imports System.Collections.Generic
583+
584+
Public Class TestWithForEachClass
585+
Private _x As Integer
586+
587+
Public Shared Sub Main()
588+
Dim x = New List(Of TestWithForEachClass)()
589+
For Each y In x
590+
With y
591+
._x = 1
592+
System.Console.Write(._x)
593+
End With
594+
y = Nothing
595+
Next
596+
End Sub
597+
End Class", @"using System;
598+
using System.Collections.Generic;
599+
600+
public partial class TestWithForEachClass
601+
{
602+
private int _x;
603+
604+
public static void Main()
605+
{
606+
var x = new List<TestWithForEachClass>();
607+
foreach (var y in x)
608+
{
609+
y._x = 1;
610+
Console.Write(y._x);
611+
y = (TestWithForEachClass)null;
612+
}
613+
}
614+
}
615+
1 target compilation errors:
616+
CS1656: Cannot assign to 'y' because it is a 'foreach iteration variable'");
617+
}
618+
533619
[Fact]
534620
public async Task NestedWithBlockAsync()
535621
{

0 commit comments

Comments
 (0)