Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor LVA and fix S1854 FP #5529

Merged
merged 7 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,24 @@ namespace SonarAnalyzer.CFG.LiveVariableAnalysis
{
public sealed class RoslynLiveVariableAnalysis : LiveVariableAnalysisBase<ControlFlowGraph, BasicBlock>
{
private readonly Dictionary<int, List<BasicBlock>> blockPredecessors = new();
private readonly Dictionary<int, List<BasicBlock>> blockSuccessors = new();

protected override BasicBlock ExitBlock => Cfg.ExitBlock;

public RoslynLiveVariableAnalysis(ControlFlowGraph cfg) : base(cfg, OriginalDeclaration(cfg.OriginalOperation)) =>
public RoslynLiveVariableAnalysis(ControlFlowGraph cfg) : base(cfg, OriginalDeclaration(cfg.OriginalOperation))
{
foreach (var ordinal in cfg.Blocks.Select(x => x.Ordinal))
{
blockPredecessors.Add(ordinal, new());
blockSuccessors.Add(ordinal, new());
}
foreach (var block in cfg.Blocks)
{
BuildBranches(block);
}
Analyze();
}

public ISymbol ParameterOrLocalSymbol(IOperation operation)
{
Expand All @@ -49,84 +63,60 @@ var _ when ILocalReferenceOperationWrapper.IsInstance(operation) => ILocalRefere
protected override IEnumerable<BasicBlock> ReversedBlocks() =>
Cfg.Blocks.Reverse();

protected override IEnumerable<BasicBlock> Successors(BasicBlock block)
protected override IEnumerable<BasicBlock> Predecessors(BasicBlock block) =>
blockPredecessors[block.Ordinal];

protected override IEnumerable<BasicBlock> Successors(BasicBlock block) =>
blockSuccessors[block.Ordinal];

protected override State ProcessBlock(BasicBlock block)
{
var ret = new RoslynState(this);
ret.ProcessBlock(Cfg, block);
return ret;
}

public override bool IsLocal(ISymbol symbol) =>
csaba-sagi-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
originalDeclaration.Equals(symbol?.ContainingSymbol);

private void BuildBranches(BasicBlock block)
{
foreach (var successor in block.Successors)
{
if (successor.Destination != null && successor.FinallyRegions.Any())
{ // When exiting finally region, redirect to finally instead of the normal destination
foreach (var finallyRegion in successor.FinallyRegions)
{
yield return Cfg.Blocks[finallyRegion.FirstBlockOrdinal];
}
}
else if (successor.Destination != null)
if (successor.Destination != null)
{
yield return successor.Destination;
// When exiting finally region, redirect to finally instead of the normal destination
AddBranch(successor.Source, successor.FinallyRegions.Any() ? Cfg.Blocks[successor.FinallyRegions.First().FirstBlockOrdinal] : successor.Destination);
}
else if (successor.Source.EnclosingRegion.Kind == ControlFlowRegionKind.Finally)
{ // Redirect exit from throw and finally to following blocks.
foreach (var trySuccessor in TryRegionSuccessors(block.EnclosingRegion))
{
yield return trySuccessor.Destination;
}
else if (successor.Source.EnclosingRegion is { Kind: ControlFlowRegionKind.Finally } finallyRegion)
{
BuildBranchesFinally(successor.Source, finallyRegion);
}
}
if (block.IsEnclosedIn(ControlFlowRegionKind.Try))
{
foreach (var catchOrFilterRegion in block.Successors.SelectMany(CatchOrFilterRegions))
{
yield return Cfg.Blocks[catchOrFilterRegion.FirstBlockOrdinal];
AddBranch(block, Cfg.Blocks[catchOrFilterRegion.FirstBlockOrdinal]);
}
}
}

protected override IEnumerable<BasicBlock> Predecessors(BasicBlock block)
private void BuildBranchesFinally(BasicBlock source, ControlFlowRegion finallyRegion)
csaba-sagi-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
{
if (block.Predecessors.Any())
// Redirect exit from throw and finally to following blocks.
foreach (var trySuccessor in TryRegionSuccessors(source.EnclosingRegion))
{
foreach (var predecessor in block.Predecessors)
{
// When exiting finally region, redirect predecessor to the source of StructuredEceptionHandling branches
if (predecessor.FinallyRegions.Any())
{
foreach (var structuredExceptionHandling in StructuredExceptionHandlinBranches(predecessor.FinallyRegions))
{
yield return structuredExceptionHandling.Source;
}
}
else
{
yield return predecessor.Source;
}
}
AddBranch(source, trySuccessor.Destination);
}
else if (block.EnclosingNonLocalLifetimeRegion() is var enclosingRegion
&& enclosingRegion.Kind == ControlFlowRegionKind.Finally
&& block.Ordinal == block.EnclosingRegion.FirstBlockOrdinal)
{
// Link first block of FinallyRegion to the source of all branches exiting that FinallyRegion
foreach (var trySuccessor in TryRegionSuccessors(enclosingRegion))
{
yield return trySuccessor.Source;
}
}

IEnumerable<ControlFlowBranch> StructuredExceptionHandlinBranches(IEnumerable<ControlFlowRegion> finallyRegions) =>
finallyRegions.Select(x => Cfg.Blocks[x.LastBlockOrdinal].Successors.SingleOrDefault(x => x.Semantics == ControlFlowBranchSemantics.StructuredExceptionHandling))
.Where(x => x != null);
}

protected override State ProcessBlock(BasicBlock block)
private void AddBranch(BasicBlock source, BasicBlock destination)
{
var ret = new RoslynState(this);
ret.ProcessBlock(Cfg, block);
return ret;
blockSuccessors[source.Ordinal].Add(destination);
blockPredecessors[destination.Ordinal].Add(source);
}

public override bool IsLocal(ISymbol symbol) =>
originalDeclaration.Equals(symbol?.ContainingSymbol);

private IEnumerable<ControlFlowBranch> TryRegionSuccessors(ControlFlowRegion finallyRegion)
{
var tryRegion = finallyRegion.EnclosingRegion.NestedRegions.Single(x => x.Kind == ControlFlowRegionKind.Try);
csaba-sagi-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ ProgramState ProcessCondition(ISymbol lockSymbol)
: RemoveLock(context, lockSymbol);
}
}

}

protected override ProgramState PostProcessSimple(SymbolicContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,8 @@ public void Catch_Loop_Propagates_LiveIn()
var context = CreateContextCS(code);
context.ValidateEntry();
context.Validate("Method(0);", new LiveOut("variable"));
context.Validate("Method(1);", new LiveIn("variable")/*Should be:, new LiveOut("variable")*/);
context.Validate("Method(2);");
context.Validate("Method(1);", new LiveIn("variable"), new LiveOut("variable"));
context.Validate("Method(2);", new LiveIn("variable"), new LiveOut("variable"));
context.Validate("Method(3);", new LiveIn("variable"), new LiveOut("variable"));
context.Validate("Method(4);", new LiveIn("variable"), new LiveOut("variable"));
context.Validate("Method(5);");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1389,7 +1389,7 @@ public void TryReturns_Loop()
var counter = 0;
while (counter < 5)
{
counter++; // Noncompliant FP related to LVA
counter++; // Compliant
try
{
SomethingThatCanThrow();
Expand Down