Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6ecf46c

Browse files
aschackmullyh-semmle
authored andcommitted
Java: Add CFG edges for switch expressions.
1 parent 9a367d9 commit 6ecf46c

3 files changed

Lines changed: 87 additions & 14 deletions

File tree

java/ql/src/semmle/code/java/Completion.qll

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ newtype Completion =
5252
(innerValue = true or innerValue = false)
5353
} or
5454
/**
55-
* The expression or statement completes via a `break` statement.
55+
* The expression or statement completes via a `break` statement without a value.
5656
*/
5757
BreakCompletion(MaybeLabel l) or
58+
/**
59+
* The expression or statement completes via a value `break` statement.
60+
*/
61+
ValueBreakCompletion(NormalOrBooleanCompletion c) or
5862
/**
5963
* The expression or statement completes via a `continue` statement.
6064
*/
@@ -64,6 +68,14 @@ newtype Completion =
6468
*/
6569
ThrowCompletion(ThrowableType tt)
6670

71+
class NormalOrBooleanCompletion extends Completion {
72+
NormalOrBooleanCompletion() {
73+
this instanceof NormalCompletion or this instanceof BooleanCompletion
74+
}
75+
76+
string toString() { result = "completion" }
77+
}
78+
6779
ContinueCompletion anonymousContinueCompletion() { result = ContinueCompletion(NoLabel()) }
6880

6981
ContinueCompletion labelledContinueCompletion(Label l) { result = ContinueCompletion(JustLabel(l)) }

java/ql/src/semmle/code/java/ControlFlowGraph.qll

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ private module ControlFlowGraphImpl {
194194
*/
195195
private predicate uncheckedExceptionFromFinally(ControlFlowNode n, ThrowableType t) {
196196
exists(TryStmt try |
197-
n.getEnclosingStmt().getParent+() = try.getBlock() or
197+
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
198198
n.(Expr).getParent*() = try.getAResource()
199199
|
200200
exists(try.getFinally()) and
@@ -208,7 +208,7 @@ private module ControlFlowGraphImpl {
208208
*/
209209
private predicate uncheckedExceptionFromCatch(ControlFlowNode n, ThrowableType t) {
210210
exists(TryStmt try, UncheckedThrowableSuperType caught |
211-
n.getEnclosingStmt().getParent+() = try.getBlock() or
211+
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
212212
n.(Expr).getParent*() = try.getAResource()
213213
|
214214
t = caught.getAnUncheckedSubtype() and
@@ -222,7 +222,7 @@ private module ControlFlowGraphImpl {
222222
*/
223223
private ThrowableType thrownInBody(TryStmt try) {
224224
exists(ControlFlowNode n | mayThrow(n, result) |
225-
n.getEnclosingStmt().getParent+() = try.getBlock() or
225+
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
226226
n.(Expr).getParent*() = try.getAResource()
227227
)
228228
}
@@ -298,6 +298,11 @@ private module ControlFlowGraphImpl {
298298
inBooleanContext(condexpr)
299299
)
300300
or
301+
exists(SwitchExpr switch |
302+
inBooleanContext(switch) and
303+
switch.getAResult() = b
304+
)
305+
or
301306
exists(ConditionalStmt condstmt | condstmt.getCondition() = b)
302307
}
303308

@@ -447,7 +452,7 @@ private module ControlFlowGraphImpl {
447452
or
448453
this.(Block).getNumStmt() = 0
449454
or
450-
this instanceof SwitchCase
455+
this instanceof SwitchCase and not this.(SwitchCase).isRule()
451456
or
452457
this instanceof EmptyStmt
453458
or
@@ -551,7 +556,7 @@ private module ControlFlowGraphImpl {
551556
// somewhere inside this loop; we don't particularly care whether that
552557
// `continue` could actually target this loop, we just want to restrict
553558
// the size of the predicate
554-
exists(ContinueStmt cnt | cnt.getParent+() = loop |
559+
exists(ContinueStmt cnt | cnt.getEnclosingStmt+() = loop |
555560
completion = anonymousContinueCompletion() or
556561
completion = labelledContinueCompletion(getLabel(loop))
557562
)
@@ -651,8 +656,9 @@ private module ControlFlowGraphImpl {
651656
*/
652657
private predicate last(ControlFlowNode n, ControlFlowNode last, Completion completion) {
653658
// Exceptions are propagated from any sub-expression.
659+
// As are any break, continue, or return completions.
654660
exists(Expr e | e.getParent() = n |
655-
last(e, last, completion) and completion = ThrowCompletion(_)
661+
last(e, last, completion) and not completion instanceof NormalOrBooleanCompletion
656662
)
657663
or
658664
// If an expression doesn't finish with a throw completion, then it executes normally with
@@ -796,6 +802,28 @@ private module ControlFlowGraphImpl {
796802
completion = NormalCompletion()
797803
)
798804
or
805+
// handle `switch` expression
806+
exists(SwitchExpr switch | switch = n |
807+
// value `break` terminates the `switch`
808+
last(switch.getAStmt(), last, ValueBreakCompletion(completion))
809+
or
810+
// any other abnormal completion is propagated
811+
last(switch.getAStmt(), last, completion) and
812+
not completion instanceof ValueBreakCompletion and
813+
completion != NormalCompletion()
814+
)
815+
or
816+
// the last node in a case rule is the last node in the right-hand side
817+
last(n.(SwitchCase).getRuleStatement(), last, completion)
818+
or
819+
// ...and if the rhs is an expression we wrap the completion as a value break
820+
exists(Completion caseCompletion |
821+
last(n.(SwitchCase).getRuleExpression(), last, caseCompletion) and
822+
if caseCompletion instanceof NormalOrBooleanCompletion
823+
then completion = ValueBreakCompletion(caseCompletion)
824+
else completion = caseCompletion
825+
)
826+
or
799827
// the last statement of a synchronized statement is the last statement of its body
800828
last(n.(SynchronizedStmt).getBlock(), last, completion)
801829
or
@@ -805,13 +833,21 @@ private module ControlFlowGraphImpl {
805833
// `throw` statements or throwing calls give rise to ` Throw` completion
806834
exists(ThrowableType tt | mayThrow(n, tt) | last = n and completion = ThrowCompletion(tt))
807835
or
808-
// `break` statements give rise to a `Break` completion
809-
exists(BreakStmt break | break = n and last = n |
836+
// `break` statements without value give rise to a `Break` completion
837+
exists(BreakStmt break | break = n and last = n and not break.hasValue() |
810838
completion = labelledBreakCompletion(MkLabel(break.getLabel()))
811839
or
812840
not exists(break.getLabel()) and completion = anonymousBreakCompletion()
813841
)
814842
or
843+
// value break statements get their completion wrapped as a value break
844+
exists(Completion caseCompletion |
845+
last(n.(BreakStmt).getValue(), last, caseCompletion) and
846+
if caseCompletion instanceof NormalOrBooleanCompletion
847+
then completion = ValueBreakCompletion(caseCompletion)
848+
else completion = caseCompletion
849+
)
850+
or
815851
// `continue` statements give rise to a `Continue` completion
816852
exists(ContinueStmt cont | cont = n and last = n |
817853
completion = labelledContinueCompletion(MkLabel(cont.getLabel()))
@@ -1055,7 +1091,32 @@ private module ControlFlowGraphImpl {
10551091
)
10561092
)
10571093
or
1058-
// No edges in a SwitchCase - the constant expression in a ConstCase isn't included in the CFG.
1094+
// Switch expressions
1095+
exists(SwitchExpr switch | completion = NormalCompletion() |
1096+
// From the entry point control is transferred first to the expression...
1097+
n = switch and result = first(switch.getExpr())
1098+
or
1099+
// ...and then to one of the cases.
1100+
last(switch.getExpr(), n, completion) and result = first(switch.getACase())
1101+
or
1102+
// Statements within a switch body execute sequentially.
1103+
exists(int i |
1104+
last(switch.getStmt(i), n, completion) and result = first(switch.getStmt(i + 1))
1105+
)
1106+
)
1107+
or
1108+
// No edges in a non-rule SwitchCase - the constant expression in a ConstCase isn't included in the CFG.
1109+
exists(SwitchCase case | completion = NormalCompletion() |
1110+
n = case and result = first(case.getRuleExpression())
1111+
or
1112+
n = case and result = first(case.getRuleStatement())
1113+
)
1114+
or
1115+
// Value break
1116+
exists(BreakStmt break | completion = NormalCompletion() |
1117+
n = break and result = first(break.getValue())
1118+
)
1119+
or
10591120
// Synchronized statements execute their expression _before_ synchronization, so the CFG reflects that.
10601121
exists(SynchronizedStmt synch | completion = NormalCompletion() |
10611122
last(synch.getExpr(), n, completion) and result = synch

java/ql/src/semmle/code/java/Statement.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -535,20 +535,20 @@ class ThrowStmt extends Stmt, @throwstmt {
535535
}
536536

537537
private Stmt findEnclosing() {
538-
result = getParent()
538+
result = getEnclosingStmt()
539539
or
540540
exists(Stmt mid |
541541
mid = findEnclosing() and
542542
not exists(this.catchClauseForThis(mid.(TryStmt))) and
543-
result = mid.getParent()
543+
result = mid.getEnclosingStmt()
544544
)
545545
}
546546

547547
private CatchClause catchClauseForThis(TryStmt try) {
548548
result = try.getACatchClause() and
549549
result.getEnclosingCallable() = this.getEnclosingCallable() and
550550
getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and
551-
not this.getParent+() = result
551+
not this.getEnclosingStmt+() = result
552552
}
553553
}
554554

@@ -588,7 +588,7 @@ class JumpStmt extends Stmt {
588588
or
589589
not exists(getSwitchExprTarget()) and
590590
result = getAPotentialTarget() and
591-
not exists(Stmt other | other = getAPotentialTarget() | other.getParent+() = result)
591+
not exists(Stmt other | other = getAPotentialTarget() | other.getEnclosingStmt+() = result)
592592
}
593593

594594
/**

0 commit comments

Comments
 (0)