Skip to content

Commit

Permalink
LVA: Add support for FlowCaptureOperation and FlowCaptureReferenceOpe…
Browse files Browse the repository at this point in the history
…ration (#9433)
  • Loading branch information
mary-georgiou-sonarsource authored Jun 24, 2024
1 parent fb37a68 commit 3e340a6
Show file tree
Hide file tree
Showing 5 changed files with 461 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace SonarAnalyzer.CFG.LiveVariableAnalysis;

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

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

Expand Down Expand Up @@ -70,10 +72,45 @@ 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 ResolveCaptures()
{
foreach (var operation in Cfg.Blocks
.Where(x => x.EnclosingRegion.EnclosingRegionOrSelf(ControlFlowRegionKind.LocalLifetime) is not null)
.SelectMany(x => x.OperationsAndBranchValue)
.ToExecutionOrder())
{
if (IFlowCaptureOperationWrapper.IsInstance(operation.Instance))
{
var flowCapture = IFlowCaptureOperationWrapper.FromOperation(operation.Instance);

if (IFlowCaptureReferenceOperationWrapper.IsInstance(flowCapture.Value)
&& IFlowCaptureReferenceOperationWrapper.FromOperation(flowCapture.Value) is var captureReference
&& flowCaptureOperations.TryGetValue(captureReference.Id, out var capturedOperations))
{
AppendFlowCaptureReference(flowCapture.Id, capturedOperations.ToArray());
}
else
{
AppendFlowCaptureReference(flowCapture.Id, flowCapture.Value);
}
}
}

void AppendFlowCaptureReference(CaptureId id, params IOperation[] operations)
{
if (!flowCaptureOperations.TryGetValue(id, out var list))
{
list = [];
flowCaptureOperations.Add(id, list);
}
list.AddRange(operations);
}
}

private void BuildBranches(BasicBlock block)
{
foreach (var successor in block.Successors)
Expand Down Expand Up @@ -190,35 +227,53 @@ 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, Dictionary<CaptureId, List<IOperation>> flowCaptureOperations)
{
foreach (var operation in block.OperationsAndBranchValue.ToReversedExecutionOrder())
foreach (var operation in block.OperationsAndBranchValue.ToReversedExecutionOrder().Select(x => x.Instance))
{
// Everything that is added to this switch needs to be considered inside ProcessCaptured as well
switch (operation.Instance.Kind)
if ((IFlowCaptureOperationWrapper.IsInstance(operation)
&& flowCaptureOperations.TryGetValue(IFlowCaptureOperationWrapper.FromOperation(operation).Id, out var captured))
|| (IFlowCaptureReferenceOperationWrapper.IsInstance(operation)
&& flowCaptureOperations.TryGetValue(IFlowCaptureReferenceOperationWrapper.FromOperation(operation).Id, out captured)))
{
foreach (var capturedOperation in captured)
{
ProcessOperation(cfg, capturedOperation);
}
}
else
{
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;
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 Expand Up @@ -295,7 +350,7 @@ private void ProcessLocalFunction(ControlFlowGraph cfg, IMethodSymbol method)
var localFunctionCfg = cfg.FindLocalFunctionCfgInScope(localFunction, owner.Cancel);
foreach (var block in localFunctionCfg.Blocks.Reverse()) // Simplified approach, ignoring branching and try/catch/finally flows
{
ProcessBlock(localFunctionCfg, block);
ProcessBlock(localFunctionCfg, block, []);
}
}
}
Expand Down
Loading

0 comments on commit 3e340a6

Please sign in to comment.