Java: Add guards-logic qltest with inline expectation.

This commit is contained in:
Anders Schack-Mulligen
2025-05-01 13:51:13 +02:00
parent aa65f54b1d
commit a0c849139c
4 changed files with 230 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
public class Guards {
static void chk() { }
static boolean g(Object lbl) { return lbl.hashCode() > 10; }
static void checkTrue(boolean b, String msg) {
if (!b) throw new Error(msg);
}
static void checkFalse(boolean b, String msg) {
checkTrue(!b, msg);
}
void t1(int[] a, String s) {
if (g("A")) {
chk(); // $ guarded=g(A):true
} else {
chk(); // $ guarded=g(A):false
}
boolean b = g(1) ? g(2) : true;
if (b != false) {
chk(); // $ guarded=...?...:...:true guarded='b != false:true' guarded=b:true
} else {
chk(); // $ guarded=...?...:...:false guarded='b != false:false' guarded=b:false guarded=g(1):true guarded=g(2):false
}
int sz = a != null ? a.length : 0;
for (int i = 0; i < sz; i++) {
chk(); // $ guarded='a != null:true' guarded='i < sz:true'
int e = a[i];
if (e > 2) break;
}
chk(); // nothing guards here
if (g(3))
s = "bar";
switch (s) {
case "bar":
chk(); // $ guarded='s:match "bar"'
break;
case "foo":
chk(); // $ guarded='s:match "foo"' guarded=g(3):false
break;
default:
chk(); // $ guarded='s:non-match "bar"' guarded='s:non-match "foo"' guarded='s:match default' guarded=g(3):false
break;
}
Object o = g(4) ? null : s;
if (o instanceof String) {
chk(); // $ guarded=...instanceof...:true guarded=g(4):false
}
}
void t2() {
checkTrue(g(1), "A");
checkFalse(g(2), "B");
chk(); // $ guarded=checkTrue(A):true guarded=g(1):true guarded=checkFalse(B):false guarded=g(2):false
}
void t3() {
boolean b = g(1) && (g(2) || g(3));
if (b) {
chk(); // $ guarded=b:true guarded='g(...) && ... \|\| ...:true' guarded=g(1):true guarded='g(...) \|\| g(...):true'
} else {
chk(); // $ guarded=b:false guarded='g(...) && ... \|\| ...:false'
}
b = g(4) || !g(5);
if (b) {
chk(); // $ guarded=b:true guarded='g(...) \|\| !...:true'
} else {
chk(); // $ guarded=b:false guarded='g(...) \|\| !...:false' guarded=g(4):false guarded=!...:false guarded=g(5):true
}
}
enum Val {
E1,
E2,
E3
}
void t4() {
Val x = null; // unique value
if (g(1)) x = Val.E1; // unique value
if (g(2)) x = Val.E2;
if (g("Alt2")) x = Val.E2;
if (g(3)) x = Val.E3; // unique value
if (x == null)
chk(); // $ guarded='x == null:true' guarded=g(3):false
switch (x) {
case E1:
chk(); // $ guarded='x:match E1' guarded=g(1):true guarded=g(3):false
break;
case E2:
chk(); // $ guarded='x:match E2' guarded=g(3):false
break;
case E3:
chk(); // $ guarded='x:match E3' guarded=g(3):true
break;
}
Object o = g(4) ? new Object() : null;
if (o == null) {
chk(); // $ guarded='o == null:true' guarded=g(4):false
} else {
chk(); // $ guarded='o == null:false' guarded=g(4):true
}
}
void t5(String foo) {
String base = foo;
if (base == null) {
base = "/user";
}
if (base.equals("/"))
chk(); // $ guarded=equals(/):true guarded='base == null:false'
}
void t6() {
Object o = null;
if (g(1)) {
o = new Object();
if (g(2)) { }
}
if (o != null) {
chk(); // $ guarded='o != null:true'
} else {
chk(); // $ guarded='o != null:false' guarded=g(1):false
}
}
}

View File

@@ -0,0 +1,56 @@
| Guards.java:16:7:16:11 | chk(...) | g(A):true |
| Guards.java:18:7:18:11 | chk(...) | g(A):false |
| Guards.java:23:7:23:11 | chk(...) | 'b != false:true' |
| Guards.java:23:7:23:11 | chk(...) | ...?...:...:true |
| Guards.java:23:7:23:11 | chk(...) | b:true |
| Guards.java:25:7:25:11 | chk(...) | 'b != false:false' |
| Guards.java:25:7:25:11 | chk(...) | ...?...:...:false |
| Guards.java:25:7:25:11 | chk(...) | b:false |
| Guards.java:25:7:25:11 | chk(...) | g(1):true |
| Guards.java:25:7:25:11 | chk(...) | g(2):false |
| Guards.java:29:7:29:11 | chk(...) | 'a != null:true' |
| Guards.java:29:7:29:11 | chk(...) | 'i < sz:true' |
| Guards.java:39:9:39:13 | chk(...) | 's:match "bar"' |
| Guards.java:42:9:42:13 | chk(...) | 's:match "foo"' |
| Guards.java:42:9:42:13 | chk(...) | g(3):false |
| Guards.java:45:9:45:13 | chk(...) | 's:match default' |
| Guards.java:45:9:45:13 | chk(...) | 's:non-match "bar"' |
| Guards.java:45:9:45:13 | chk(...) | 's:non-match "foo"' |
| Guards.java:45:9:45:13 | chk(...) | g(3):false |
| Guards.java:51:7:51:11 | chk(...) | ...instanceof...:true |
| Guards.java:51:7:51:11 | chk(...) | g(4):false |
| Guards.java:58:5:58:9 | chk(...) | checkFalse(B):false |
| Guards.java:58:5:58:9 | chk(...) | checkTrue(A):true |
| Guards.java:58:5:58:9 | chk(...) | g(1):true |
| Guards.java:58:5:58:9 | chk(...) | g(2):false |
| Guards.java:64:7:64:11 | chk(...) | 'g(...) && ... \|\| ...:true' |
| Guards.java:64:7:64:11 | chk(...) | 'g(...) \|\| g(...):true' |
| Guards.java:64:7:64:11 | chk(...) | b:true |
| Guards.java:64:7:64:11 | chk(...) | g(1):true |
| Guards.java:66:7:66:11 | chk(...) | 'g(...) && ... \|\| ...:false' |
| Guards.java:66:7:66:11 | chk(...) | b:false |
| Guards.java:70:7:70:11 | chk(...) | 'g(...) \|\| !...:true' |
| Guards.java:70:7:70:11 | chk(...) | b:true |
| Guards.java:72:7:72:11 | chk(...) | !...:false |
| Guards.java:72:7:72:11 | chk(...) | 'g(...) \|\| !...:false' |
| Guards.java:72:7:72:11 | chk(...) | b:false |
| Guards.java:72:7:72:11 | chk(...) | g(4):false |
| Guards.java:72:7:72:11 | chk(...) | g(5):true |
| Guards.java:89:7:89:11 | chk(...) | 'x == null:true' |
| Guards.java:89:7:89:11 | chk(...) | g(3):false |
| Guards.java:92:9:92:13 | chk(...) | 'x:match E1' |
| Guards.java:92:9:92:13 | chk(...) | g(1):true |
| Guards.java:92:9:92:13 | chk(...) | g(3):false |
| Guards.java:95:9:95:13 | chk(...) | 'x:match E2' |
| Guards.java:95:9:95:13 | chk(...) | g(3):false |
| Guards.java:98:9:98:13 | chk(...) | 'x:match E3' |
| Guards.java:98:9:98:13 | chk(...) | g(3):true |
| Guards.java:103:7:103:11 | chk(...) | 'o == null:true' |
| Guards.java:103:7:103:11 | chk(...) | g(4):false |
| Guards.java:105:7:105:11 | chk(...) | 'o == null:false' |
| Guards.java:105:7:105:11 | chk(...) | g(4):true |
| Guards.java:115:7:115:11 | chk(...) | 'base == null:false' |
| Guards.java:115:7:115:11 | chk(...) | equals(/):true |
| Guards.java:125:7:125:11 | chk(...) | 'o != null:true' |
| Guards.java:127:7:127:11 | chk(...) | 'o != null:false' |
| Guards.java:127:7:127:11 | chk(...) | g(1):false |

View File

@@ -0,0 +1,42 @@
import java
import semmle.code.java.controlflow.Guards
import codeql.util.Boolean
string ppGuard(Guard g, Boolean branch) {
exists(MethodCall mc, Literal s |
mc = g and
mc.getAnArgument() = s and
result = mc.getMethod().getName() + "(" + s.getValue() + ")" + ":" + branch
)
or
exists(BinaryExpr bin |
bin = g and
result = "'" + bin.getLeftOperand() + bin.getOp() + bin.getRightOperand() + ":" + branch + "'"
)
or
exists(SwitchCase cc, Expr s, string match, string value |
cc = g and
cc.getSelectorExpr() = s and
(
cc.(ConstCase).getValue().toString() = value
or
cc instanceof DefaultCase and value = "default"
) and
if branch = true then match = ":match " else match = ":non-match "
|
result = "'" + s.toString() + match + value + "'"
)
}
query predicate guarded(MethodCall mc, string guard) {
mc.getMethod().hasName("chk") and
exists(Guard g, BasicBlock bb, boolean branch |
g.controls(bb, branch) and
mc.getBasicBlock() = bb
|
guard = ppGuard(g, branch)
or
not exists(ppGuard(g, branch)) and
guard = g.toString() + ":" + branch
)
}

View File

@@ -0,0 +1,2 @@
query: GuardsInline.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql