C#: Clean up.

This commit is contained in:
Anders Schack-Mulligen
2025-09-26 10:30:53 +02:00
parent c2d21e95b9
commit 6cfadbfe90
4 changed files with 4 additions and 804 deletions

View File

@@ -294,13 +294,7 @@ module ControlFlow {
class Split = Splitting::Split;
class FinallySplit = Splitting::FinallySplitting::FinallySplit;
class ExceptionHandlerSplit = Splitting::ExceptionHandlerSplitting::ExceptionHandlerSplit;
class BooleanSplit = Splitting::BooleanSplitting::BooleanSplit;
class LoopSplit = Splitting::LoopSplitting::LoopSplit;
}
class BasicBlock = BBs::BasicBlock;

View File

@@ -292,87 +292,6 @@ private module LogicInput implements GuardsImpl::LogicInputSig {
module Guards = GuardsImpl::Logic<LogicInput>;
/*
* Temporary debug predicates:
*/
predicate debug_newcontrols(Guards::Guard g, BasicBlock bb, GuardValue v) { g.valueControls(bb, v) }
predicate debug_oldconvert(Guards::Guard g, BasicBlock bb, GuardValue v) {
exists(AbstractValue av |
g.(Guard).controlsBasicBlock(bb, av) and
debug_convVals(av, v)
)
or
debug_oldconvertCase(_, _, g, bb, v)
}
predicate debug_oldconvertCase(Guard g1, MatchValue av, Guards::Guard g2, BasicBlock bb, GuardValue v) {
g1.controlsBasicBlock(bb, av) and
av.getCase() = g2 and
if av.isMatch() then v.asBooleanValue() = true else v.asBooleanValue() = false
}
predicate debug_caseconverted(Guard g1, Guards::Guard g, BasicBlock bb, GuardValue v) {
debug_oldconvertCase(g1, _, g, bb, v) and
2 <= strictcount(Guard g0 | debug_oldconvertCase(g0, _, g, bb, v))
}
predicate debug_useless(Guards::Guard g, BasicBlock bb, GuardValue v) {
debug_oldconvert(g, bb, v) and
Guards::InternalUtil::exprHasValue(g, v)
}
predicate debug_compare(int eq, int oldconv, int oldnonconv, int added, int new) {
eq =
count(Guards::Guard g, BasicBlock bb, GuardValue v |
debug_newcontrols(g, bb, v) and debug_oldconvert(g, bb, v)
) and
oldconv =
count(Guards::Guard g, BasicBlock bb, GuardValue v |
debug_oldconvert(g, bb, v) and
not debug_newcontrols(g, bb, v) and
not debug_useless(g, bb, v)
) and
oldnonconv =
count(Guard g, BasicBlock bb, AbstractValue av |
g.controlsBasicBlock(bb, av) and
not debug_convVals(av, _) and
not debug_oldconvertCase(g, av, _, bb, _)
// Remaining that are not converted:
// av instanceof EmptyCollectionValue
) and
added =
count(Guards::Guard g, BasicBlock bb, GuardValue v |
debug_newcontrols(g, bb, v) and
not debug_oldconvert(g, bb, v) and
not debug_newGv(v)
) and
new =
count(Guards::Guard g, BasicBlock bb, GuardValue v |
debug_newcontrols(g, bb, v) and
not debug_oldconvert(g, bb, v) and
debug_newGv(v)
)
}
predicate debug_newGv(GuardValue v) {
v.isThrowsException() or
v.getDualValue().isThrowsException() or
exists(v.getDualValue().asIntValue()) or
v.isIntRange(_, _)
}
predicate debug_convVals(AbstractValue av, GuardValue gv) {
av.(AbstractValues::BooleanValue).getValue() = gv.asBooleanValue()
or
av.(AbstractValues::IntegerValue).getValue() = gv.asIntValue()
or
av.(AbstractValues::NullValue).isNull() and gv.isNullValue()
or
av.(AbstractValues::NullValue).isNonNull() and gv.isNonNullValue()
}
/** An expression whose value may control the execution of another element. */
class Guard extends Expr {
Guard() { isGuard(this, _) }

View File

@@ -26,10 +26,7 @@ private module Cached {
TInitializerSplitKind() or
TConditionalCompletionSplitKind() or
TAssertionSplitKind() or
TFinallySplitKind(int nestLevel) { nestLevel = any(Statements::TryStmtTree t).nestLevel() } or
TExceptionHandlerSplitKind() or
TBooleanSplitKind(BooleanSplitting::BooleanSplitSubKind kind) { kind.startsSplit(_) } or
TLoopSplitKind(LoopSplitting::AnalyzableLoopStmt loop)
TExceptionHandlerSplitKind()
cached
newtype TSplit =
@@ -39,17 +36,7 @@ private module Cached {
exists(a.getExpr(i)) and
success in [false, true]
} or
TFinallySplit(FinallySplitting::FinallySplitType type, int nestLevel) {
nestLevel = any(Statements::TryStmtTree t).nestLevel() and
none()
} or
TExceptionHandlerSplit(ExceptionClass ec) or
TBooleanSplit(BooleanSplitting::BooleanSplitSubKind kind, boolean branch) {
kind.startsSplit(_) and
branch in [false, true] and
none()
} or
TLoopSplit(LoopSplitting::AnalyzableLoopStmt loop) { none() }
TExceptionHandlerSplit(ExceptionClass ec)
}
import Cached
@@ -463,241 +450,6 @@ module AssertionSplitting {
}
}
module FinallySplitting {
/**
* The type of a split `finally` node.
*
* The type represents one of the possible ways of entering a `finally`
* block. For example, if a `try` statement ends with a `return` statement,
* then the `finally` block must end with a `return` as well (provided that
* the `finally` block exits normally).
*/
class FinallySplitType extends Cfg::SuccessorType {
FinallySplitType() { not this instanceof Cfg::ConditionalSuccessor }
/** Holds if this split type matches entry into a `finally` block with completion `c`. */
predicate isSplitForEntryCompletion(Completion c) {
if c instanceof NormalCompletion
then
// If the entry into the `finally` block completes with any normal completion,
// it simply means normal execution after the `finally` block
this instanceof Cfg::DirectSuccessor
else this = c.getAMatchingSuccessorType()
}
}
/** A control flow element that belongs to a `finally` block. */
private class FinallyAstNode extends AstNode {
private Statements::TryStmtTree try;
FinallyAstNode() { this = try.getAFinallyDescendant() }
/** Gets the immediate `try` block that this node belongs to. */
Statements::TryStmtTree getTryStmt() { result = try }
/** Holds if this node is the entry node in the `finally` block it belongs to. */
predicate isEntryNode() { first(try.(TryStmt).getFinally(), this) }
}
/**
* A split for elements belonging to a `finally` block, which determines how to
* continue execution after leaving the `finally` block. For example, in
*
* ```csharp
* try
* {
* if (!M())
* throw new Exception();
* }
* finally
* {
* Log.Write("M failed");
* }
* ```
*
* all control flow nodes in the `finally` block have two splits: one representing
* normal execution of the `try` block (when `M()` returns `true`), and one
* representing exceptional execution of the `try` block (when `M()` returns `false`).
*/
class FinallySplit extends Split, TFinallySplit {
private FinallySplitType type;
private int nestLevel;
FinallySplit() { this = TFinallySplit(type, nestLevel) }
/**
* Gets the type of this `finally` split, that is, how to continue execution after the
* `finally` block.
*/
FinallySplitType getType() { result = type }
/** Gets the `finally` nesting level. */
int getNestLevel() { result = nestLevel }
override string toString() {
if type instanceof Cfg::DirectSuccessor
then result = ""
else
if nestLevel > 0
then result = "finally(" + nestLevel + "): " + type.toString()
else result = "finally: " + type.toString()
}
}
private int getListOrder(FinallySplitKind kind) {
result = AssertionSplitting::getNextListOrder() + kind.getNestLevel()
}
int getNextListOrder() {
result = max([getListOrder(_) + 1, AssertionSplitting::getNextListOrder()])
}
private class FinallySplitKind extends SplitKind, TFinallySplitKind {
private int nestLevel;
FinallySplitKind() { this = TFinallySplitKind(nestLevel) }
/** Gets the `finally` nesting level. */
int getNestLevel() { result = nestLevel }
override int getListOrder() { result = getListOrder(this) }
override string toString() { result = "Finally (" + nestLevel + ")" }
}
pragma[nomagic]
private predicate hasEntry0(AstNode pred, FinallyAstNode succ, int nestLevel, Completion c) {
succ.isEntryNode() and
nestLevel = succ.getTryStmt().nestLevel() and
succ(pred, succ, c)
}
private class FinallySplitImpl extends SplitImpl instanceof FinallySplit {
override FinallySplitKind getKind() { result.getNestLevel() = super.getNestLevel() }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
hasEntry0(pred, succ, super.getNestLevel(), c) and
super.getType().isSplitForEntryCompletion(c)
}
override predicate hasEntryScope(CfgScope scope, AstNode first) { none() }
/**
* Holds if this split applies to control flow element `pred`, where `pred`
* is a valid predecessor.
*/
private predicate appliesToPredecessor(AstNode pred) {
this.appliesTo(pred) and
(succ(pred, _, _) or scopeLast(_, pred, _))
}
pragma[noinline]
private predicate exit0(AstNode pred, Statements::TryStmtTree try, int nestLevel, Completion c) {
this.appliesToPredecessor(pred) and
nestLevel = try.nestLevel() and
last(try, pred, c)
}
/**
* Holds if `pred` may exit this split with completion `c`. The Boolean
* `inherited` indicates whether `c` is an inherited completion from a `try`/
* `catch` block.
*/
private predicate exit(AstNode pred, Completion c, boolean inherited) {
exists(TryStmt try, FinallySplitType type |
this.exit0(pred, try, super.getNestLevel(), c) and
type = super.getType()
|
if last(try.getFinally(), pred, c)
then
// Finally block can itself exit with completion `c`: either `c` must
// match this split, `c` must be an abnormal completion, or this split
// does not require another completion to be recovered
inherited = false and
(
type = c.getAMatchingSuccessorType()
or
not c instanceof NormalCompletion
or
type instanceof Cfg::DirectSuccessor
)
else (
// Finally block can exit with completion `c` inherited from try/catch
// block: must match this split
inherited = true and
type = c.getAMatchingSuccessorType() and
not type instanceof Cfg::DirectSuccessor
)
)
or
// If this split is normal, and an outer split can exit based on an inherited
// completion, we need to exit this split as well. For example, in
//
// ```csharp
// bool done;
// try
// {
// if (b1) throw new ExceptionA();
// }
// finally
// {
// try
// {
// if (b2) throw new ExceptionB();
// }
// finally
// {
// done = true;
// }
// }
// ```
//
// if the outer split for `done = true` is `ExceptionA` and the inner split
// is "normal" (corresponding to `b1 = true` and `b2 = false`), then the inner
// split must be able to exit with an `ExceptionA` completion.
this.appliesToPredecessor(pred) and
exists(FinallySplit outer |
outer.getNestLevel() = super.getNestLevel() - 1 and
outer.(FinallySplitImpl).exit(pred, c, inherited) and
super.getType() instanceof Cfg::DirectSuccessor and
inherited = true
)
}
override predicate hasExit(AstNode pred, AstNode succ, Completion c) {
succ(pred, succ, c) and
(
this.exit(pred, c, _)
or
this.exit(pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _)
)
}
override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) {
scopeLast(scope, last, c) and
(
this.exit(last, c, _)
or
this.exit(last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _)
)
}
override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) {
this.appliesSucc(pred, succ, c) and
succ =
any(FinallyAstNode fcfe |
if fcfe.isEntryNode()
then
// entering a nested `finally` block
fcfe.getTryStmt().nestLevel() > super.getNestLevel()
else
// staying in the same (possibly nested) `finally` block as `pred`
fcfe.getTryStmt().nestLevel() >= super.getNestLevel()
)
}
}
}
module ExceptionHandlerSplitting {
private newtype TMatch =
TAlways() or
@@ -751,12 +503,12 @@ module ExceptionHandlerSplitting {
}
private class ExceptionHandlerSplitKind extends SplitKind, TExceptionHandlerSplitKind {
override int getListOrder() { result = FinallySplitting::getNextListOrder() }
override int getListOrder() { result = AssertionSplitting::getNextListOrder() }
override string toString() { result = "ExceptionHandler" }
}
int getNextListOrder() { result = FinallySplitting::getNextListOrder() + 1 }
int getNextListOrder() { result = AssertionSplitting::getNextListOrder() + 1 }
private class ExceptionHandlerSplitImpl extends SplitImpl instanceof ExceptionHandlerSplit {
override ExceptionHandlerSplitKind getKind() { any() }
@@ -873,452 +625,3 @@ module ExceptionHandlerSplitting {
}
}
}
module BooleanSplitting {
private import semmle.code.csharp.controlflow.internal.PreBasicBlocks
/** A sub-classification of Boolean splits. */
abstract class BooleanSplitSubKind extends TBooleanSplitSubKind {
/**
* Holds if the branch taken by condition `cb1` should be recorded in
* this split, and the recorded value determines the branch taken by a
* later condition `cb2`, possibly inverted.
*
* For example, in
*
* ```csharp
* var b = GetB();
* if (b)
* Console.WriteLine("b is true");
* if (!b)
* Console.WriteLine("b is false");
* ```
*
* the branch taken in the condition on line 2 can be recorded, and the
* recorded value will determine the branch taken in the condition on line 4.
*/
abstract predicate correlatesConditions(ConditionBlock cb1, ConditionBlock cb2, boolean inverted);
/** Holds if control flow element `cfe` starts a split of this kind. */
predicate startsSplit(AstNode cfe) {
this.correlatesConditions(any(ConditionBlock cb | cb.getLastNode() = cfe), _, _)
}
/**
* Holds if basic block `bb` can reach a condition correlated with a
* split of this kind.
*/
abstract predicate canReachCorrelatedCondition(PreBasicBlock bb);
/** Gets the callable that this Boolean split kind belongs to. */
abstract Callable getEnclosingCallable();
/** Gets a textual representation of this Boolean split kind. */
abstract string toString();
/** Gets the location of this Boolean split kind. */
abstract Location getLocation();
}
/**
* A Boolean split that records the value of a Boolean SSA variable.
*
* For example, in
*
* ```csharp
* var b = GetB();
* if (b)
* Console.WriteLine("b is true");
* if (!b)
* Console.WriteLine("b is false");
* ```
*
* there is a Boolean split on the SSA variable for `b` at line 1.
*/
class SsaBooleanSplitSubKind extends BooleanSplitSubKind, TSsaBooleanSplitSubKind {
private PreSsa::Definition def;
SsaBooleanSplitSubKind() { this = TSsaBooleanSplitSubKind(def) }
/**
* Holds if condition `cb` is a read of the SSA variable in this split.
*/
private predicate defCondition(ConditionBlock cb) { cb.getLastNode() = def.getARead() }
/**
* Holds if condition `cb` is a read of the SSA variable in this split,
* and `cb` can be reached from `read` without passing through another
* condition that reads the same SSA variable.
*/
private predicate defConditionReachableFromRead(ConditionBlock cb, AssignableRead read) {
this.defCondition(cb) and
read = cb.getLastNode()
or
exists(AssignableRead mid | this.defConditionReachableFromRead(cb, mid) |
PreSsa::adjacentReadPairSameVar(read, mid) and
not this.defCondition(read)
)
}
/**
* Holds if condition `cb` is a read of the SSA variable in this split,
* and `cb` can be reached from the SSA definition without passing through
* another condition that reads the same SSA variable.
*/
private predicate firstDefCondition(ConditionBlock cb) {
this.defConditionReachableFromRead(cb, def.getAFirstRead())
}
override predicate correlatesConditions(ConditionBlock cb1, ConditionBlock cb2, boolean inverted) {
this.firstDefCondition(cb1) and
exists(AssignableRead read1, AssignableRead read2 |
read1 = cb1.getLastNode() and
PreSsa::adjacentReadPairSameVar+(read1, read2) and
read2 = cb2.getLastNode() and
inverted = false
)
}
override predicate canReachCorrelatedCondition(PreBasicBlock bb) {
this.correlatesConditions(_, bb, _) and
not def.getBasicBlock() = bb
or
exists(PreBasicBlock mid | this.canReachCorrelatedCondition(mid) |
bb = mid.getAPredecessor() and
not def.getBasicBlock() = bb
)
}
override Callable getEnclosingCallable() { result = def.getBasicBlock().getEnclosingCallable() }
override string toString() { result = def.getSourceVariable().toString() }
override Location getLocation() { result = def.getLocation() }
}
/**
* A split for elements that can reach a condition where this split determines
* the Boolean value that the condition evaluates to. For example, in
*
* ```csharp
* if (b)
* Console.WriteLine("b is true");
* if (!b)
* Console.WriteLine("b is false");
* ```
*
* all control flow nodes on line 2 and line 3 have two splits: one representing
* that the condition on line 1 took the `true` branch, and one representing that
* the condition on line 1 took the `false` branch.
*/
class BooleanSplit extends Split, TBooleanSplit {
private BooleanSplitSubKind kind;
private boolean branch;
BooleanSplit() { this = TBooleanSplit(kind, branch) }
/** Gets the kind of this Boolean split. */
BooleanSplitSubKind getSubKind() { result = kind }
/** Gets the branch taken in this split. */
boolean getBranch() { result = branch }
override string toString() {
exists(int line |
line = kind.getLocation().getStartLine() and
result = kind.toString() + " (line " + line + "): " + branch.toString()
)
}
}
private int getListOrder(BooleanSplitSubKind kind) {
exists(Callable c, int r | c = kind.getEnclosingCallable() |
result = r + ExceptionHandlerSplitting::getNextListOrder() - 1 and
kind =
rank[r](BooleanSplitSubKind kind0, Location l |
kind0.getEnclosingCallable() = c and
kind0.startsSplit(_) and
l = kind0.getLocation()
|
kind0 order by l.getStartLine(), l.getStartColumn(), kind0.toString()
)
)
}
int getNextListOrder() {
result = max([getListOrder(_) + 1, ExceptionHandlerSplitting::getNextListOrder()])
}
private class BooleanSplitKind extends SplitKind, TBooleanSplitKind {
private BooleanSplitSubKind kind;
BooleanSplitKind() { this = TBooleanSplitKind(kind) }
/** Gets the sub kind of this Boolean split kind. */
BooleanSplitSubKind getSubKind() { result = kind }
override int getListOrder() { result = getListOrder(kind) }
override string toString() { result = kind.toString() }
}
pragma[nomagic]
private predicate hasEntry0(
AstNode pred, AstNode succ, BooleanSplitSubKind kind, boolean b, Completion c
) {
kind.startsSplit(pred) and
succ(pred, succ, c) and
b = c.getInnerCompletion().(BooleanCompletion).getValue()
}
private class BooleanSplitImpl extends SplitImpl instanceof BooleanSplit {
override BooleanSplitKind getKind() { result.getSubKind() = super.getSubKind() }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
hasEntry0(pred, succ, super.getSubKind(), super.getBranch(), c)
}
override predicate hasEntryScope(CfgScope scope, AstNode first) { none() }
private ConditionBlock getACorrelatedCondition(boolean inverted) {
super.getSubKind().correlatesConditions(_, result, inverted)
}
/**
* Holds if this split applies to basic block `bb`, where the the last
* element of `bb` can have completion `c`.
*/
private predicate appliesToBlock(PreBasicBlock bb, Completion c) {
this.appliesTo(bb) and
exists(AstNode last | last = bb.getLastNode() |
(succ(last, _, c) or scopeLast(_, last, c)) and
// Respect the value recorded in this split for all correlated conditions
forall(boolean inverted | bb = this.getACorrelatedCondition(inverted) |
c.getInnerCompletion() instanceof BooleanCompletion
implies
c.getInnerCompletion().(BooleanCompletion).getValue() =
super.getBranch().booleanXor(inverted)
)
)
}
override predicate hasExit(AstNode pred, AstNode succ, Completion c) {
exists(PreBasicBlock bb | this.appliesToBlock(bb, c) |
pred = bb.getLastNode() and
succ(pred, succ, c) and
// Exit this split if we can no longer reach a correlated condition
not super.getSubKind().canReachCorrelatedCondition(succ)
)
}
override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) {
exists(PreBasicBlock bb | this.appliesToBlock(bb, c) |
last = bb.getLastNode() and
scopeLast(scope, last, c)
)
}
override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) {
exists(PreBasicBlock bb, Completion c0 | this.appliesToBlock(bb, c0) |
pred = bb.getAnElement() and
this.appliesSucc(pred, succ, c) and
(
pred = bb.getLastNode()
implies
(
// We must still be able to reach a correlated condition to stay in this split
super.getSubKind().canReachCorrelatedCondition(succ) and
c = c0
)
)
)
}
}
}
module LoopSplitting {
private import semmle.code.csharp.controlflow.Guards as Guards
private import PreBasicBlocks
/** Holds if `ce` is guarded by a (non-)empty check, as specified by `v`. */
private predicate emptinessGuarded(
Guards::Guard g, Guards::EnumerableCollectionExpr ce,
Guards::AbstractValues::EmptyCollectionValue v
) {
exists(PreBasicBlock bb | Guards::Internal::preControls(g, bb, v) |
PreSsa::adjacentReadPairSameVar(g, ce) and
bb.getAnElement() = ce
)
}
/**
* A loop where the body is guaranteed to be executed at least once, and hence
* can be unrolled in the control flow graph, or where the body is guaranteed
* to never be executed, and hence can be removed from the control flow graph.
*/
abstract class AnalyzableLoopStmt extends LoopStmt {
/** Holds if the step `pred --c--> succ` should start the split. */
abstract predicate start(AstNode pred, AstNode succ, Completion c);
/** Holds if the step `pred --c--> succ` should stop the split. */
abstract predicate stop(AstNode pred, AstNode succ, Completion c);
/**
* Holds if any step `pred --c--> _` should be pruned from the control flow graph.
*/
abstract predicate pruneLoopCondition(AstNode pred, ConditionalCompletion c);
/**
* Holds if the body is guaranteed to be executed at least once. If not, the
* body is guaranteed to never be executed.
*/
abstract predicate isUnroll();
}
private class AnalyzableForeachStmt extends AnalyzableLoopStmt, ForeachStmt {
Guards::AbstractValues::EmptyCollectionValue v;
AnalyzableForeachStmt() {
/*
* We use `unique` to avoid degenerate cases like
* ```csharp
* if (xs.Length == 0)
* return;
* if (xs.Length > 0)
* return;
* foreach (var x in xs)
* ....
* ```
* where the iterator expression `xs` is guarded by both an emptiness check
* and a non-emptiness check.
*/
v =
unique(Guards::AbstractValues::EmptyCollectionValue v0 |
emptinessGuarded(_, this.getIterableExpr(), v0)
or
this.getIterableExpr() = v0.getAnExpr()
|
v0
)
}
override predicate start(AstNode pred, AstNode succ, Completion c) {
last(this.getIterableExpr(), pred, c) and
succ = this
}
override predicate stop(AstNode pred, AstNode succ, Completion c) {
pred = this and
succ(pred, succ, c)
}
override predicate pruneLoopCondition(AstNode pred, ConditionalCompletion c) {
pred = this and
c = any(EmptinessCompletion ec | if v.isEmpty() then not ec.isEmpty() else ec.isEmpty())
}
override predicate isUnroll() { v.isNonEmpty() }
}
/**
* A split for loops where the body is guaranteed to be executed at least once, or
* guaranteed to never be executed. For example, in
*
* ```csharp
* void M(string[] args)
* {
* if (args.Length == 0)
* return;
* foreach (var arg in args)
* System.Console.WriteLine(args);
* }
* ```
*
* the `foreach` loop is guaranteed to be executed at least once, as a result of the
* `args.Length == 0` check.
*/
class LoopSplit extends Split, TLoopSplit {
AnalyzableLoopStmt loop;
LoopSplit() { this = TLoopSplit(loop) }
override string toString() {
if loop.isUnroll()
then result = "unroll (line " + loop.getLocation().getStartLine() + ")"
else result = "skip (line " + loop.getLocation().getStartLine() + ")"
}
}
pragma[noinline]
private Callable enclosingCallable(AnalyzableLoopStmt loop) {
result = loop.getEnclosingCallable()
}
private int getListOrder(AnalyzableLoopStmt loop) {
exists(Callable c, int r | c = enclosingCallable(loop) |
result = r + BooleanSplitting::getNextListOrder() - 1 and
loop =
rank[r](AnalyzableLoopStmt loop0, Location l |
enclosingCallable(loop0) = c and
l = loop0.getLocation()
|
loop0 order by l.getStartLine(), l.getStartColumn()
)
)
}
int getNextListOrder() {
result = max([getListOrder(_) + 1, BooleanSplitting::getNextListOrder()])
}
private class LoopSplitKind extends SplitKind, TLoopSplitKind {
private AnalyzableLoopStmt loop;
LoopSplitKind() { this = TLoopSplitKind(loop) }
override int getListOrder() { result = getListOrder(loop) }
override string toString() { result = "Unroll" }
}
private class LoopUnrollingSplitImpl extends SplitImpl instanceof LoopSplit {
AnalyzableLoopStmt loop;
LoopUnrollingSplitImpl() { this = TLoopSplit(loop) }
override LoopSplitKind getKind() { result = TLoopSplitKind(loop) }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
loop.start(pred, succ, c)
}
override predicate hasEntryScope(CfgScope scope, AstNode first) { none() }
/**
* Holds if this split applies to control flow element `pred`, where `pred`
* is a valid predecessor.
*/
private predicate appliesToPredecessor(AstNode pred, Completion c) {
this.appliesTo(pred) and
(succ(pred, _, c) or scopeLast(_, pred, c)) and
not loop.pruneLoopCondition(pred, c)
}
override predicate hasExit(AstNode pred, AstNode succ, Completion c) {
this.appliesToPredecessor(pred, c) and
loop.stop(pred, succ, c)
}
override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) {
this.appliesToPredecessor(last, c) and
scopeLast(scope, last, c)
}
override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) {
this.appliesToPredecessor(pred, c) and
this.appliesSucc(pred, succ, c) and
not loop.stop(pred, succ, c)
}
}
}

View File

@@ -5,22 +5,6 @@ import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as Impl
import semmle.code.csharp.controlflow.internal.Splitting as Splitting
import Nodes
query predicate booleanNode(ElementNode e, BooleanSplit split) { split = e.getASplit() }
class MyFinallySplitControlFlowNode extends ElementNode {
MyFinallySplitControlFlowNode() {
exists(Splitting::FinallySplitting::FinallySplitType type |
type = this.getASplit().(FinallySplit).getType()
|
not type instanceof DirectSuccessor
)
}
Impl::Statements::TryStmtTree getTryStmt() { this.getAstNode() = result.getAFinallyDescendant() }
}
query predicate finallyNode(MyFinallySplitControlFlowNode f, TryStmt try) { try = f.getTryStmt() }
query predicate entryPoint(Callable c, SourceControlFlowElement cfn) {
c.getEntryPoint().getASuccessor().getAstNode() = cfn
}