diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
index fb8ad0ddae840..9331dab2b5c2e 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
@@ -2541,6 +2541,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperator node)
do
{
stack.Push(binary);
+ EnterRegionIfNeeded(binary);
binary = binary.Left as BoundBinaryOperator;
}
while (binary != null && !binary.OperatorKind.IsLogical() && binary.InterpolatedStringHandlerData is null);
@@ -2550,6 +2551,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperator node)
}
#nullable enable
+ /// Nested left-associative binary operators, pushed on from outermost to innermost.
protected virtual void VisitBinaryOperatorChildren(ArrayBuilder stack)
{
var binary = stack.Pop();
@@ -2592,6 +2594,7 @@ protected virtual void VisitBinaryOperatorChildren(ArrayBuilder().ToArray();
+ Assert.Equal(2, decls.Length);
+ var decl = decls[1];
+ Assert.Equal("int k = i + j + 1;", decl.ToString());
+ var flowAnalysis = model.AnalyzeDataFlow(decl);
+ Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+
+ var binOps = tree.GetRoot().DescendantNodes().OfType().ToArray();
+ Assert.Equal(2, binOps.Length);
+ var add = binOps[0];
+ Assert.Equal("i + j + 1", add.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(add);
+ Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+
+ add = binOps[1];
+ Assert.Equal("i + j", add.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(add);
+ Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+ }
+
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
+ public void FourBinaryOperands()
+ {
+ var comp = CreateCompilation("""
+ class Program
+ {
+ private static void Repro()
+ {
+ int i = 1, j = 2, k = 3, l = 4;
+ _ = i + j + k + l;
+ }
+ }
+ """);
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.CommonSyntaxTrees[0];
+ var model = comp.GetSemanticModel(tree);
+
+ var decl = tree.GetRoot().DescendantNodes().OfType().Single();
+ Assert.Equal("_ = i + j + k + l;", decl.ToString());
+ var flowAnalysis = model.AnalyzeDataFlow(decl);
+ Assert.Equal("i, j, k, l", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+
+ var binOps = tree.GetRoot().DescendantNodes().OfType().ToArray();
+ Assert.Equal(3, binOps.Length);
+ var add = binOps[0];
+ Assert.Equal("i + j + k + l", add.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(add);
+ Assert.Equal("i, j, k, l", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+
+ add = binOps[1];
+ Assert.Equal("i + j + k", add.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(add);
+ Assert.Equal("i, j, k", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+
+ add = binOps[2];
+ Assert.Equal("i + j", add.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(add);
+ Assert.Equal("i, j", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+ }
+
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
+ public void BinaryOpConditionalAccess()
+ {
+ var comp = CreateCompilation("""
+ class C
+ {
+ public bool M(out int x) { x = 0; return false; }
+
+ private static void Repro(C c)
+ {
+ const bool y = true;
+ const bool z = true;
+ int x;
+ _ = c?.M(out x) == y == z;
+ }
+ }
+ """);
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.CommonSyntaxTrees[0];
+ var model = comp.GetSemanticModel(tree);
+
+ var decl = tree.GetRoot().DescendantNodes().OfType().Last();
+ Assert.Equal("_ = c?.M(out x) == y == z;", decl.ToString());
+ var flowAnalysis = model.AnalyzeDataFlow(decl);
+ Assert.Equal("c, y, z", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+ Assert.Equal("x", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
+
+ var binOps = tree.GetRoot().DescendantNodes().OfType().ToArray();
+ Assert.Equal(2, binOps.Length);
+
+ var binOp = binOps[0];
+ Assert.Equal("c?.M(out x) == y == z", binOp.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(binOp);
+ Assert.Equal("c, y, z", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+ Assert.Equal("x", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
+
+ binOp = binOps[1];
+ Assert.Equal("c?.M(out x) == y", binOp.ToString());
+ flowAnalysis = model.AnalyzeDataFlow(binOp);
+ Assert.Equal("c, y", GetSymbolNamesJoined(flowAnalysis.ReadInside));
+ Assert.Equal("x", GetSymbolNamesJoined(flowAnalysis.WrittenInside));
+ }
}
}
diff --git a/src/Compilers/VisualBasic/Test/Semantic/FlowAnalysis/RegionAnalysisTests.vb b/src/Compilers/VisualBasic/Test/Semantic/FlowAnalysis/RegionAnalysisTests.vb
index 7525a6f8f50b8..00673802748a4 100644
--- a/src/Compilers/VisualBasic/Test/Semantic/FlowAnalysis/RegionAnalysisTests.vb
+++ b/src/Compilers/VisualBasic/Test/Semantic/FlowAnalysis/RegionAnalysisTests.vb
@@ -9994,6 +9994,38 @@ End Module
capturedOutside)
End Sub
+
+
+ Public Sub NestedBinaryOperator()
+ Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime(
+
+
+Module Program
+ Sub M(i As Integer, j As Integer, k As Integer, l As Integer)
+ Dim x = i + j + k + l
+ End Sub
+End Module
+
+ )
+
+ Dim tree = compilation.SyntaxTrees.First()
+ Dim model = compilation.GetSemanticModel(tree)
+ Dim nodes = tree.GetRoot().DescendantNodes().OfType(Of BinaryExpressionSyntax)().ToArray()
+ Assert.Equal(3, nodes.Length)
+
+ Assert.Equal("i + j + k + l", nodes(0).ToString())
+ Dim dataFlowResults = model.AnalyzeDataFlow(nodes(0))
+ Assert.Equal("i, j, k, l", GetSymbolNamesJoined(dataFlowResults.ReadInside))
+
+ Assert.Equal("i + j + k", nodes(1).ToString())
+ dataFlowResults = model.AnalyzeDataFlow(nodes(1))
+ Assert.Equal("i, j, k", GetSymbolNamesJoined(dataFlowResults.ReadInside))
+
+ Assert.Equal("i + j", nodes(2).ToString())
+ dataFlowResults = model.AnalyzeDataFlow(nodes(2))
+ Assert.Equal("i, j", GetSymbolNamesJoined(dataFlowResults.ReadInside))
+ End Sub
+
#End Region
End Class
diff --git a/src/Features/CSharpTest/ExtractMethod/ExtractMethodCodeRefactoringTests.cs b/src/Features/CSharpTest/ExtractMethod/ExtractMethodCodeRefactoringTests.cs
index d88137fcd0c5a..ab777c113f97c 100644
--- a/src/Features/CSharpTest/ExtractMethod/ExtractMethodCodeRefactoringTests.cs
+++ b/src/Features/CSharpTest/ExtractMethod/ExtractMethodCodeRefactoringTests.cs
@@ -6048,4 +6048,35 @@ struct S1
}
""");
}
+
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38087")]
+ public async Task TestPartialSelectionOfArithmeticExpression()
+ {
+ await TestInRegularAndScript1Async(
+ """
+ class C
+ {
+ private void Repro()
+ {
+ int i = 1, j = 2;
+ int k = [|i + j|] + 1;
+ }
+ }
+ """,
+ """
+ class C
+ {
+ private void Repro()
+ {
+ int i = 1, j = 2;
+ int k = {|Rename:NewMethod|}(i, j) + 1;
+ }
+
+ private static int NewMethod(int i, int j)
+ {
+ return i + j;
+ }
+ }
+ """);
+ }
}