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; + } + } + """); + } }