提交 e7f0317e 编写于 作者: N Neal Gafter 提交者: Julien Couvreur

Fix region tracking for regions with local functions (#37450)

* Fix region tracking for regions with local functions

Fixes #37427

* Add to test pre review comment.
上级 67d75c04
......@@ -202,6 +202,8 @@ protected void Unsplit()
_nonMonotonicTransfer = nonMonotonicTransferFunction;
}
protected bool TrackingRegions => _trackRegions;
protected abstract string Dump(TLocalState state);
protected string Dump()
......
......@@ -7,7 +7,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
// Note: this code has a copy-and-paste sibling in AbstractRegionDataFlowPass.
// Note: this code has a copy-and-paste sibling in AbstractRegionControlFlowPass.
// Any fix to one should be applied to the other.
internal abstract class AbstractRegionDataFlowPass : DefiniteAssignmentPass
{
......
......@@ -58,6 +58,12 @@ private HashSet<Symbol> Analyze(ref bool badRegion)
return _dataFlowsOut;
}
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
{
_dataFlowsOut.Clear();
return base.Scan(ref badRegion);
}
protected override void EnterRegion()
{
// to handle loops properly, we must assume that every variable that flows in is
......
......@@ -1477,23 +1477,23 @@ public override BoundNode VisitBlock(BoundBlock node)
private void VisitStatementsWithLocalFunctions(BoundBlock block)
{
// Visit the statements in two phases:
// 1. Local function declarations
// 2. Everything else
//
// The idea behind visiting local functions first is
// that we may be able to gather the captured variables
// they read and write ahead of time in a single pass, so
// when they are used by other statements in the block we
// won't have to recompute the set by doing multiple passes.
//
// If the local functions contain forward calls to other local
// functions then we may have to do another pass regardless,
// but hopefully that will be an uncommon case in real-world code.
// First phase
if (!block.LocalFunctions.IsDefaultOrEmpty)
{
if (!TrackingRegions && !block.LocalFunctions.IsDefaultOrEmpty)
{
// Visit the statements in two phases:
// 1. Local function declarations
// 2. Everything else
//
// The idea behind visiting local functions first is
// that we may be able to gather the captured variables
// they read and write ahead of time in a single pass, so
// when they are used by other statements in the block we
// won't have to recompute the set by doing multiple passes.
//
// If the local functions contain forward calls to other local
// functions then we may have to do another pass regardless,
// but hopefully that will be an uncommon case in real-world code.
// First phase
foreach (var stmt in block.Statements)
{
if (stmt.Kind == BoundKind.LocalFunctionStatement)
......@@ -1501,12 +1501,19 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block)
VisitAlways(stmt);
}
}
}
// Second phase
foreach (var stmt in block.Statements)
// Second phase
foreach (var stmt in block.Statements)
{
if (stmt.Kind != BoundKind.LocalFunctionStatement)
{
VisitStatement(stmt);
}
}
}
else
{
if (stmt.Kind != BoundKind.LocalFunctionStatement)
foreach (var stmt in block.Statements)
{
VisitStatement(stmt);
}
......
......@@ -10,13 +10,16 @@
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
/// <summary>
/// Tests for the region analysis APIs.
/// </summary>
/// <remarks>
/// Please add your tests to other files if possible:
/// * FlowDiagnosticTests.cs - all tests on Diagnostics
/// * IterationJumpYieldStatementTests.cs - while, do, for, foreach, break, continue, goto, iterator (yield break, yield return)
/// * TryLockUsingStatementTests.cs - try-catch-finally, lock, &amp; using statement
/// * PatternsVsRegions.cs - region analysis tests for pattern matching
/// </summary>
public partial class FlowAnalysisTests : FlowTestBase
/// </remarks>
public partial class RegionAnalysisTests : FlowTestBase
{
#region "Expressions"
......@@ -6360,6 +6363,64 @@ void Method(out object test)
Assert.Equal("this, test, a", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
}
[Fact, WorkItem(37427, "https://github.com/dotnet/roslyn/issues/37427")]
public void RegionWithLocalFunctions()
{
// local functions inside the region
var s1 = @"
class A
{
static void M(int p)
{
int i, j;
i = 1;
/*<bind>*/
int L1() => 1;
int k;
j = i;
int L2() => 2;
/*</bind>*/
k = j;
}
}
";
// local functions outside the region
var s2 = @"
class A
{
static void M(int p)
{
int i, j;
i = 1;
int L1() => 1;
/*<bind>*/
int k;
j = i;
/*</bind>*/
int L2() => 2;
k = j;
}
}
";
foreach (var s in new[] { s1, s2 })
{
var analysisResults = CompileAndAnalyzeDataFlowStatements(s);
var dataFlowAnalysisResults = analysisResults;
Assert.True(dataFlowAnalysisResults.Succeeded);
Assert.Equal("k", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
Assert.Equal("j", GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
Assert.Equal("j", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
Assert.Equal("j", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("j", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
Assert.Equal("p, i, k", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
}
}
#endregion
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册