Skip to content

Commit

Permalink
Support flow captures
Browse files Browse the repository at this point in the history
  • Loading branch information
mary-georgiou-sonarsource committed Jun 19, 2024
1 parent 88e7856 commit 5c57827
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Collections.Concurrent;
using SonarAnalyzer.CFG.Roslyn;

namespace SonarAnalyzer.CFG.LiveVariableAnalysis;

public sealed class RoslynLiveVariableAnalysis : LiveVariableAnalysisBase<ControlFlowGraph, BasicBlock>
{
private readonly ConcurrentDictionary<CaptureId, ConcurrentBag<IOperation>> flowCaptureOperations = [];
private readonly Dictionary<int, List<BasicBlock>> blockPredecessors = [];
private readonly Dictionary<int, List<BasicBlock>> blockSuccessors = [];

Expand All @@ -41,6 +43,7 @@ public RoslynLiveVariableAnalysis(ControlFlowGraph cfg, CancellationToken cancel
{
BuildBranches(block);
}
LiveCaptureAnalysis(cfg);
Analyze();
}

Expand Down Expand Up @@ -70,10 +73,34 @@ protected override IEnumerable<BasicBlock> Successors(BasicBlock block) =>
protected override State ProcessBlock(BasicBlock block)
{
var ret = new RoslynState(this);
ret.ProcessBlock(Cfg, block);
ret.ProcessBlock(Cfg, block, flowCaptureOperations);
return ret;
}

private void LiveCaptureAnalysis(ControlFlowGraph cfg)
{
foreach (var operation in cfg.Blocks.Where(x => x.IsEnclosedIn(ControlFlowRegionKind.LocalLifetime)).SelectMany(x => x.Operations).ToExecutionOrder())
{
if (IFlowCaptureOperationWrapper.IsInstance(operation.Instance))
{
var flowCapture = IFlowCaptureOperationWrapper.FromOperation(operation.Instance);
if (!IFlowCaptureReferenceOperationWrapper.IsInstance(flowCapture.Value))
{
_ = flowCaptureOperations.TryGetValue(flowCapture.Id, out var captureOperations);
var value = captureOperations ?? [];
value.Add(flowCapture.Value);
flowCaptureOperations.TryAdd(flowCapture.Id, value);
}
else
{
var flowCaptureReference = IFlowCaptureReferenceOperationWrapper.FromOperation(flowCapture.Value);
_ = flowCaptureOperations.TryGetValue(flowCaptureReference.Id, out var captureOperations);
flowCaptureOperations.TryAdd(flowCapture.Id, captureOperations);
}
}
}
}

private void BuildBranches(BasicBlock block)
{
foreach (var successor in block.Successors)
Expand Down Expand Up @@ -177,35 +204,65 @@ private sealed class RoslynState : State
public RoslynState(RoslynLiveVariableAnalysis owner) =>
this.owner = owner;

public void ProcessBlock(ControlFlowGraph cfg, BasicBlock block)
public void ProcessBlock(ControlFlowGraph cfg, BasicBlock block, ConcurrentDictionary<CaptureId, ConcurrentBag<IOperation>> flowCaptureOperations = null)
{
foreach (var operation in block.OperationsAndBranchValue.ToReversedExecutionOrder())
if (flowCaptureOperations is null)
{
// Everything that is added to this switch needs to be considered inside ProcessCaptured as well
switch (operation.Instance.Kind)
flowCaptureOperations = new();
}
foreach (var op in block.OperationsAndBranchValue.ToReversedExecutionOrder())
{
var operation = op.Instance;
if (IFlowCaptureOperationWrapper.IsInstance(operation)
&& flowCaptureOperations.TryGetValue(IFlowCaptureOperationWrapper.FromOperation(operation).Id, out var captureOperations))
{
case OperationKindEx.LocalReference:
ProcessParameterOrLocalReference(ILocalReferenceOperationWrapper.FromOperation(operation.Instance));
break;
case OperationKindEx.ParameterReference:
ProcessParameterOrLocalReference(IParameterReferenceOperationWrapper.FromOperation(operation.Instance));
break;
case OperationKindEx.SimpleAssignment:
ProcessSimpleAssignment(ISimpleAssignmentOperationWrapper.FromOperation(operation.Instance));
break;
case OperationKindEx.FlowAnonymousFunction:
ProcessFlowAnonymousFunction(cfg, IFlowAnonymousFunctionOperationWrapper.FromOperation(operation.Instance));
break;
case OperationKindEx.Invocation:
ProcessLocalFunction(cfg, IInvocationOperationWrapper.FromOperation(operation.Instance).TargetMethod);
break;
case OperationKindEx.MethodReference:
ProcessLocalFunction(cfg, IMethodReferenceOperationWrapper.FromOperation(operation.Instance).Method);
break;
foreach (var captureOpeation in captureOperations)
{
ProcessOperation(cfg, captureOpeation);
}
}
else if (IFlowCaptureReferenceOperationWrapper.IsInstance(operation)
&& flowCaptureOperations.TryGetValue(IFlowCaptureReferenceOperationWrapper.FromOperation(operation).Id, out captureOperations))
{
foreach (var captureOpeation in captureOperations)
{
ProcessOperation(cfg, captureOpeation);
}

}
else
{
ProcessOperation(cfg, operation);
}
}
}

private void ProcessOperation(ControlFlowGraph cfg, IOperation operation)
{
// Everything that is added to this switch needs to be considered inside ProcessCaptured as well
switch (operation.Kind)
{
case OperationKindEx.LocalReference:
ProcessParameterOrLocalReference(ILocalReferenceOperationWrapper.FromOperation(operation));
break;
case OperationKindEx.ParameterReference:
ProcessParameterOrLocalReference(IParameterReferenceOperationWrapper.FromOperation(operation));
break;
case OperationKindEx.SimpleAssignment:
ProcessSimpleAssignment(ISimpleAssignmentOperationWrapper.FromOperation(operation));
break;
case OperationKindEx.FlowAnonymousFunction:
ProcessFlowAnonymousFunction(cfg, IFlowAnonymousFunctionOperationWrapper.FromOperation(operation));
break;
case OperationKindEx.Invocation:
ProcessLocalFunction(cfg, IInvocationOperationWrapper.FromOperation(operation).TargetMethod);
break;
case OperationKindEx.MethodReference:
ProcessLocalFunction(cfg, IMethodReferenceOperationWrapper.FromOperation(operation).Method);
break;
}
}

private void ProcessParameterOrLocalReference(IOperationWrapper reference)
{
if (owner.ParameterOrLocalSymbol(reference.WrappedOperation) is { } symbol)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ int LocalFunction(int arg)
var context = CreateContextCS(code, "LocalFunction");
context.ValidateEntry(LiveIn("arg"), LiveOut("arg"));
context.Validate("variable", LiveIn("arg"), LiveOut("arg"));
context.Validate("LocalFunction(arg - 1)", LiveIn("arg"));
context.Validate("0");
context.Validate("LocalFunction(arg - 1)", LiveIn("arg"), LiveOut("arg"));
context.Validate("0", LiveIn("arg"), LiveOut("arg"));
}

[TestMethod]
Expand Down
Loading

0 comments on commit 5c57827

Please sign in to comment.