Skip to content

Commit cbdd492

Browse files
authored
Merge pull request #8582 from Marcono1234/marcono1234/JumpStmt-superclass
Java: Make `JumpStmt` a proper superclass
2 parents fd2904d + b21f077 commit cbdd492

File tree

11 files changed

+67
-21
lines changed

11 files changed

+67
-21
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: fix
3+
---
4+
* The QL class `JumpStmt` has been made the superclass of `BreakStmt`, `ContinueStmt` and `YieldStmt`. This allows directly using its inherited predicates without having to explicitly cast to `JumpStmt` first.

java/ql/lib/semmle/code/java/Expr.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,7 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
13991399
Expr getAResult() {
14001400
result = this.getACase().getRuleExpression()
14011401
or
1402-
exists(YieldStmt yield | yield.(JumpStmt).getTarget() = this and result = yield.getValue())
1402+
exists(YieldStmt yield | yield.getTarget() = this and result = yield.getValue())
14031403
}
14041404

14051405
/** Gets a printable representation of this expression. */

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

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,10 @@ class ThrowStmt extends Stmt, @throwstmt {
570570
override string getAPrimaryQlClass() { result = "ThrowStmt" }
571571
}
572572

573-
/** A `break`, `yield` or `continue` statement. */
574-
class JumpStmt extends Stmt {
575-
JumpStmt() {
576-
this instanceof BreakStmt or
577-
this instanceof YieldStmt or
578-
this instanceof ContinueStmt
579-
}
573+
private class JumpStmt_ = @breakstmt or @yieldstmt or @continuestmt;
580574

575+
/** A `break`, `yield` or `continue` statement. */
576+
class JumpStmt extends Stmt, JumpStmt_ {
581577
/**
582578
* Gets the labeled statement that this `break` or
583579
* `continue` statement refers to, if any.
@@ -598,12 +594,7 @@ class JumpStmt extends Stmt {
598594
)
599595
}
600596

601-
private SwitchExpr getSwitchExprTarget() { result = this.(YieldStmt).getParent+() }
602-
603597
private StmtParent getEnclosingTarget() {
604-
result = this.getSwitchExprTarget()
605-
or
606-
not exists(this.getSwitchExprTarget()) and
607598
result = this.getAPotentialTarget() and
608599
not exists(Stmt other | other = this.getAPotentialTarget() | other.getEnclosingStmt+() = result)
609600
}
@@ -612,14 +603,15 @@ class JumpStmt extends Stmt {
612603
* Gets the statement or `switch` expression that this `break`, `yield` or `continue` jumps to.
613604
*/
614605
StmtParent getTarget() {
606+
// Note: This implementation only considers `break` and `continue`; YieldStmt overrides this predicate
615607
result = this.getLabelTarget()
616608
or
617609
not exists(this.getLabelTarget()) and result = this.getEnclosingTarget()
618610
}
619611
}
620612

621613
/** A `break` statement. */
622-
class BreakStmt extends Stmt, @breakstmt {
614+
class BreakStmt extends JumpStmt, @breakstmt {
623615
/** Gets the label targeted by this `break` statement, if any. */
624616
string getLabel() { namestrings(result, _, this) }
625617

@@ -640,12 +632,21 @@ class BreakStmt extends Stmt, @breakstmt {
640632
/**
641633
* A `yield` statement.
642634
*/
643-
class YieldStmt extends Stmt, @yieldstmt {
635+
class YieldStmt extends JumpStmt, @yieldstmt {
644636
/**
645637
* Gets the value of this `yield` statement.
646638
*/
647639
Expr getValue() { result.getParent() = this }
648640

641+
/**
642+
* Gets the `switch` expression target of this `yield` statement.
643+
*/
644+
override SwitchExpr getTarget() {
645+
// Get the innermost enclosing SwitchExpr; this works because getParent() is defined for Stmt and
646+
// therefore won't proceed after the innermost SwitchExpr (due to it being an Expr)
647+
result = this.getParent+()
648+
}
649+
649650
override string pp() { result = "yield ..." }
650651

651652
override string toString() { result = "yield ..." }
@@ -656,7 +657,7 @@ class YieldStmt extends Stmt, @yieldstmt {
656657
}
657658

658659
/** A `continue` statement. */
659-
class ContinueStmt extends Stmt, @continuestmt {
660+
class ContinueStmt extends JumpStmt, @continuestmt {
660661
/** Gets the label targeted by this `continue` statement, if any. */
661662
string getLabel() { namestrings(result, _, this) }
662663

java/ql/src/Likely Bugs/Statements/ContinueInFalseLoop.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ import java
1616
from DoStmt do, ContinueStmt continue
1717
where
1818
do.getCondition().(BooleanLiteral).getBooleanValue() = false and
19-
continue.(JumpStmt).getTarget() = do
19+
continue.getTarget() = do
2020
select continue, "This 'continue' never re-runs the loop - the $@ is always false.",
2121
do.getCondition(), "loop condition"

java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ predicate loopExit(LoopStmt loop, Stmt exit) {
3232
exit.getEnclosingStmt*() = loop.getBody() and
3333
(
3434
exit instanceof ReturnStmt or
35-
exit.(BreakStmt).(JumpStmt).getTarget() = loop.getEnclosingStmt*()
35+
exit.(BreakStmt).getTarget() = loop.getEnclosingStmt*()
3636
)
3737
}
3838

java/ql/src/Security/CWE/CWE-129/ArraySizing.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ class PointlessLoop extends WhileStmt {
4444
PointlessLoop() {
4545
this.getCondition().(BooleanLiteral).getBooleanValue() = true and
4646
// The only `break` must be the last statement.
47-
forall(BreakStmt break | break.(JumpStmt).getTarget() = this |
47+
forall(BreakStmt break | break.getTarget() = this |
4848
this.getStmt().(BlockStmt).getLastStmt() = break
4949
) and
5050
// No `continue` statements.
51-
not exists(ContinueStmt continue | continue.(JumpStmt).getTarget() = this)
51+
not exists(ContinueStmt continue | continue.getTarget() = this)
5252
}
5353
}
5454

java/ql/src/Security/CWE/CWE-835/InfiniteLoop.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ predicate loopCondition(LoopStmt loop, Expr cond, boolean polarity) {
2626
ifstmt.getEnclosingStmt*() = loop.getBody() and
2727
ifstmt.getCondition() = cond and
2828
(
29-
exit.(BreakStmt).(JumpStmt).getTarget() = loop or
29+
exit.(BreakStmt).getTarget() = loop or
3030
exit.(ReturnStmt).getEnclosingStmt*() = loop.getBody()
3131
) and
3232
(

java/ql/test/library-tests/stmts/JumpTargets.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
| stmts/A.java:18:5:18:12 | yield ... | stmts/A.java:16:10:16:18 | switch (...) |
2+
| stmts/A.java:22:6:22:13 | yield ... | stmts/A.java:20:14:20:22 | switch (...) |
3+
| stmts/A.java:25:6:25:13 | yield ... | stmts/A.java:20:14:20:22 | switch (...) |
4+
| stmts/A.java:34:7:34:14 | yield ... | stmts/A.java:16:10:16:18 | switch (...) |
5+
| stmts/A.java:37:5:37:13 | yield ... | stmts/A.java:16:10:16:18 | switch (...) |
16
| stmts/B.java:8:5:8:10 | break | stmts/B.java:6:3:6:26 | for (...;...;...) |
27
| stmts/B.java:10:5:10:13 | continue | stmts/B.java:6:3:6:26 | for (...;...;...) |
38
| stmts/B.java:13:6:13:11 | break | stmts/B.java:11:4:11:17 | while (...) |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
| stmts/A.java:6:3:6:10 | case ... |
22
| stmts/A.java:8:3:8:10 | case ... |
33
| stmts/A.java:10:3:10:10 | default |
4+
| stmts/A.java:17:4:17:12 | case ... |
5+
| stmts/A.java:20:4:20:12 | case ... |
6+
| stmts/A.java:21:5:21:13 | case ... |
7+
| stmts/A.java:24:5:24:14 | default |
8+
| stmts/A.java:28:4:28:12 | case ... |
9+
| stmts/A.java:29:4:29:13 | default |
10+
| stmts/A.java:32:6:32:14 | case ... |
11+
| stmts/A.java:33:6:33:14 | case ... |
412
| stmts/B.java:21:5:21:12 | case ... |
513
| stmts/B.java:23:5:23:12 | case ... |
614
| stmts/B.java:25:5:25:12 | case ... |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -source 14 -target 14

java/ql/test/library-tests/stmts/stmts/A.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,31 @@ public int foo(int x) {
1111
return x;
1212
}
1313
}
14+
15+
public int nestedSwitchExpr(int x, int y) {
16+
return switch(x) {
17+
case 1 -> {
18+
yield 1;
19+
}
20+
case 2 -> switch(y) {
21+
case 0 -> {
22+
yield 0;
23+
}
24+
default -> {
25+
yield 1;
26+
}
27+
};
28+
case 3 -> 3;
29+
default -> {
30+
// SwitchStmt inside SwitchExpr
31+
switch (y) {
32+
case 1 -> System.out.println("1");
33+
case 2 -> {
34+
yield 2;
35+
}
36+
}
37+
yield -1;
38+
}
39+
};
40+
}
1441
}

0 commit comments

Comments
 (0)