Skip to content

Commit b2b9e32

Browse files
Call Enter/LeaveRegion APIs for binary expressions (#76412)
Co-authored-by: Cyrus Najmabadi <[email protected]>
1 parent 71bdd3b commit b2b9e32

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperator node)
25412541
do
25422542
{
25432543
stack.Push(binary);
2544+
EnterRegionIfNeeded(binary);
25442545
binary = binary.Left as BoundBinaryOperator;
25452546
}
25462547
while (binary != null && !binary.OperatorKind.IsLogical() && binary.InterpolatedStringHandlerData is null);
@@ -2550,6 +2551,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperator node)
25502551
}
25512552

25522553
#nullable enable
2554+
/// <param name="stack">Nested left-associative binary operators, pushed on from outermost to innermost.</param>
25532555
protected virtual void VisitBinaryOperatorChildren(ArrayBuilder<BoundBinaryOperator> stack)
25542556
{
25552557
var binary = stack.Pop();
@@ -2592,6 +2594,7 @@ protected virtual void VisitBinaryOperatorChildren(ArrayBuilder<BoundBinaryOpera
25922594
SetConditionalState(isNullConstant == isEquals(binary)
25932595
? (State, stateWhenNotNull)
25942596
: (stateWhenNotNull, State));
2597+
LeaveRegionIfNeeded(binary);
25952598

25962599
if (stack.Count == 0)
25972600
{
@@ -2609,6 +2612,7 @@ protected virtual void VisitBinaryOperatorChildren(ArrayBuilder<BoundBinaryOpera
26092612
Unsplit();
26102613
VisitRvalue(binary.Right);
26112614
}
2615+
LeaveRegionIfNeeded(binary);
26122616

26132617
if (stack.Count == 0)
26142618
{

src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ private NullableWalker(
462462
bool isSpeculative = false)
463463
: base(compilation, symbol, node, EmptyStructTypeCache.CreatePrecise(), trackUnassignments: true)
464464
{
465+
Debug.Assert(!TrackingRegions);
465466
Debug.Assert(!useDelegateInvokeParameterTypes || delegateInvokeMethodOpt is object);
466467
Debug.Assert(baseOrThisInitializer is null or { MethodKind: MethodKind.Constructor });
467468

src/Compilers/CSharp/Test/Emit3/FlowAnalysis/RegionAnalysisTests.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14147,5 +14147,128 @@ ref struct RS
1414714147
Assert.Null(GetSymbolNamesJoined(flowAnalysis.ReadInside));
1414814148
Assert.Equal("this", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
1414914149
}
14150+
14151+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
14152+
public void Repro_38087()
14153+
{
14154+
var comp = CreateCompilation("""
14155+
class Program
14156+
{
14157+
private static void Repro()
14158+
{
14159+
int i = 1, j = 2;
14160+
int k = i + j + 1;
14161+
}
14162+
}
14163+
""");
14164+
comp.VerifyEmitDiagnostics();
14165+
14166+
var tree = comp.CommonSyntaxTrees[0];
14167+
var model = comp.GetSemanticModel(tree);
14168+
14169+
var decls = tree.GetRoot().DescendantNodes().OfType<LocalDeclarationStatementSyntax>().ToArray();
14170+
Assert.Equal(2, decls.Length);
14171+
var decl = decls[1];
14172+
Assert.Equal("int k = i + j + 1;", decl.ToString());
14173+
var flowAnalysis = model.AnalyzeDataFlow(decl);
14174+
Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14175+
14176+
var binOps = tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().ToArray();
14177+
Assert.Equal(2, binOps.Length);
14178+
var add = binOps[0];
14179+
Assert.Equal("i + j + 1", add.ToString());
14180+
flowAnalysis = model.AnalyzeDataFlow(add);
14181+
Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14182+
14183+
add = binOps[1];
14184+
Assert.Equal("i + j", add.ToString());
14185+
flowAnalysis = model.AnalyzeDataFlow(add);
14186+
Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14187+
}
14188+
14189+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
14190+
public void FourBinaryOperands()
14191+
{
14192+
var comp = CreateCompilation("""
14193+
class Program
14194+
{
14195+
private static void Repro()
14196+
{
14197+
int i = 1, j = 2, k = 3, l = 4;
14198+
_ = i + j + k + l;
14199+
}
14200+
}
14201+
""");
14202+
comp.VerifyEmitDiagnostics();
14203+
14204+
var tree = comp.CommonSyntaxTrees[0];
14205+
var model = comp.GetSemanticModel(tree);
14206+
14207+
var decl = tree.GetRoot().DescendantNodes().OfType<ExpressionStatementSyntax>().Single();
14208+
Assert.Equal("_ = i + j + k + l;", decl.ToString());
14209+
var flowAnalysis = model.AnalyzeDataFlow(decl);
14210+
Assert.Equal("i, j, k, l", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14211+
14212+
var binOps = tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().ToArray();
14213+
Assert.Equal(3, binOps.Length);
14214+
var add = binOps[0];
14215+
Assert.Equal("i + j + k + l", add.ToString());
14216+
flowAnalysis = model.AnalyzeDataFlow(add);
14217+
Assert.Equal("i, j, k, l", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14218+
14219+
add = binOps[1];
14220+
Assert.Equal("i + j + k", add.ToString());
14221+
flowAnalysis = model.AnalyzeDataFlow(add);
14222+
Assert.Equal("i, j, k", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14223+
14224+
add = binOps[2];
14225+
Assert.Equal("i + j", add.ToString());
14226+
flowAnalysis = model.AnalyzeDataFlow(add);
14227+
Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14228+
}
14229+
14230+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
14231+
public void BinaryOpConditionalAccess()
14232+
{
14233+
var comp = CreateCompilation("""
14234+
class C
14235+
{
14236+
public bool M(out int x) { x = 0; return false; }
14237+
14238+
private static void Repro(C c)
14239+
{
14240+
const bool y = true;
14241+
const bool z = true;
14242+
int x;
14243+
_ = c?.M(out x) == y == z;
14244+
}
14245+
}
14246+
""");
14247+
comp.VerifyEmitDiagnostics();
14248+
14249+
var tree = comp.CommonSyntaxTrees[0];
14250+
var model = comp.GetSemanticModel(tree);
14251+
14252+
var decl = tree.GetRoot().DescendantNodes().OfType<ExpressionStatementSyntax>().Last();
14253+
Assert.Equal("_ = c?.M(out x) == y == z;", decl.ToString());
14254+
var flowAnalysis = model.AnalyzeDataFlow(decl);
14255+
Assert.Equal("c, y, z", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14256+
Assert.Equal("x", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
14257+
14258+
var binOps = tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().ToArray();
14259+
Assert.Equal(2, binOps.Length);
14260+
14261+
var binOp = binOps[0];
14262+
Assert.Equal("c?.M(out x) == y == z", binOp.ToString());
14263+
flowAnalysis = model.AnalyzeDataFlow(binOp);
14264+
Assert.Equal("c, y, z", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14265+
Assert.Equal("x", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
14266+
14267+
binOp = binOps[1];
14268+
Assert.Equal("c?.M(out x) == y", binOp.ToString());
14269+
flowAnalysis = model.AnalyzeDataFlow(binOp);
14270+
Assert.Equal("c, y", GetSymbolNamesJoined(flowAnalysis.ReadInside));
14271+
Assert.Equal("x", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
14272+
}
1415014273
}
1415114274
}

src/Compilers/VisualBasic/Test/Semantic/FlowAnalysis/RegionAnalysisTests.vb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9994,6 +9994,38 @@ End Module
99949994
capturedOutside)
99959995
End Sub
99969996

9997+
<WorkItem("https://github.com/dotnet/roslyn/issues/38087")>
9998+
<Fact()>
9999+
Public Sub NestedBinaryOperator()
10000+
Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(
10001+
<compilation>
10002+
<file name="a.vb">
10003+
Module Program
10004+
Sub M(i As Integer, j As Integer, k As Integer, l As Integer)
10005+
Dim x = i + j + k + l
10006+
End Sub
10007+
End Module
10008+
</file>
10009+
</compilation>)
10010+
10011+
Dim tree = compilation.SyntaxTrees.First()
10012+
Dim model = compilation.GetSemanticModel(tree)
10013+
Dim nodes = tree.GetRoot().DescendantNodes().OfType(Of BinaryExpressionSyntax)().ToArray()
10014+
Assert.Equal(3, nodes.Length)
10015+
10016+
Assert.Equal("i + j + k + l", nodes(0).ToString())
10017+
Dim dataFlowResults = model.AnalyzeDataFlow(nodes(0))
10018+
Assert.Equal("i, j, k, l", GetSymbolNamesJoined(dataFlowResults.ReadInside))
10019+
10020+
Assert.Equal("i + j + k", nodes(1).ToString())
10021+
dataFlowResults = model.AnalyzeDataFlow(nodes(1))
10022+
Assert.Equal("i, j, k", GetSymbolNamesJoined(dataFlowResults.ReadInside))
10023+
10024+
Assert.Equal("i + j", nodes(2).ToString())
10025+
dataFlowResults = model.AnalyzeDataFlow(nodes(2))
10026+
Assert.Equal("i, j", GetSymbolNamesJoined(dataFlowResults.ReadInside))
10027+
End Sub
10028+
999710029
#End Region
999810030

999910031
End Class

src/Features/CSharpTest/ExtractMethod/ExtractMethodCodeRefactoringTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6048,4 +6048,35 @@ struct S1
60486048
}
60496049
""");
60506050
}
6051+
6052+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
6053+
public async Task TestPartialSelectionOfArithmeticExpression()
6054+
{
6055+
await TestInRegularAndScript1Async(
6056+
"""
6057+
class C
6058+
{
6059+
private void Repro()
6060+
{
6061+
int i = 1, j = 2;
6062+
int k = [|i + j|] + 1;
6063+
}
6064+
}
6065+
""",
6066+
"""
6067+
class C
6068+
{
6069+
private void Repro()
6070+
{
6071+
int i = 1, j = 2;
6072+
int k = {|Rename:NewMethod|}(i, j) + 1;
6073+
}
6074+
6075+
private static int NewMethod(int i, int j)
6076+
{
6077+
return i + j;
6078+
}
6079+
}
6080+
""");
6081+
}
60516082
}

0 commit comments

Comments
 (0)