Skip to content

Commit 1d02753

Browse files
committed
[Java]: Add precondition support for testing library asserts
1 parent 7674535 commit 1d02753

File tree

10 files changed

+202
-9
lines changed

10 files changed

+202
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added guard preconditon support for assertion methods for popular testing libraries (eg. Junit 4, Junit 5, TestNG).

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private module ControlFlowGraphImpl {
185185
* Bind `t` to an unchecked exception that may occur in a precondition check.
186186
*/
187187
private predicate uncheckedExceptionFromMethod(MethodAccess ma, ThrowableType t) {
188-
conditionCheck(ma, _) and
188+
conditionCheck(ma, _, _) and
189189
(t instanceof TypeError or t instanceof TypeRuntimeException)
190190
}
191191

java/ql/lib/semmle/code/java/controlflow/Guards.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class Guard extends ExprParent {
8686
or
8787
this instanceof SwitchCase
8888
or
89-
conditionCheck(this, _)
89+
conditionCheck(this, _, _)
9090
}
9191

9292
/** Gets the immediately enclosing callable whose body contains this guard. */

java/ql/lib/semmle/code/java/controlflow/internal/GuardsLogic.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ predicate implies_v1(Guard g1, boolean b1, Guard g2, boolean b2) {
5757
or
5858
g1.(DefaultCase).getSwitch().getAConstCase() = g2 and b1 = true and b2 = false
5959
or
60-
exists(MethodAccess check | check = g1 |
61-
conditionCheck(check, _) and
62-
g2 = check.getArgument(0) and
60+
exists(MethodAccess check, int argIndex | check = g1 |
61+
conditionCheck(check, argIndex, _) and
62+
g2 = check.getArgument(argIndex) and
6363
b1 = [true, false] and
6464
b2 = b1
6565
)

java/ql/lib/semmle/code/java/controlflow/internal/Preconditions.qll

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import java
1111
* is equal to `checkTrue` and throws otherwise.
1212
*/
1313
predicate conditionCheckMethod(Method m, boolean checkTrue) {
14+
conditionCheckMethod(m, 0, checkTrue)
15+
or
1416
m.getDeclaringType().hasQualifiedName("com.google.common.base", "Preconditions") and
1517
checkTrue = true and
1618
(m.hasName("checkArgument") or m.hasName("checkState"))
@@ -19,6 +21,24 @@ predicate conditionCheckMethod(Method m, boolean checkTrue) {
1921
checkTrue = true and
2022
(m.hasName("isTrue") or m.hasName("validState"))
2123
or
24+
m.getDeclaringType().hasQualifiedName("org.junit", "Assume") and
25+
checkTrue = true and
26+
m.hasName("assumeTrue")
27+
or
28+
m.getDeclaringType().hasQualifiedName("org.junit.jupiter.api", "Assertions") and
29+
(
30+
checkTrue = true and m.hasName("assertTrue")
31+
or
32+
checkTrue = false and m.hasName("assertFalse")
33+
)
34+
or
35+
m.getDeclaringType().hasQualifiedName("org.junit.jupiter.api", "Assumptions") and
36+
(
37+
checkTrue = true and m.hasName("assumeTrue")
38+
or
39+
checkTrue = false and m.hasName("assumeFalse")
40+
)
41+
or
2242
exists(Parameter p, IfStmt ifstmt, Expr cond |
2343
p = m.getParameter(0) and
2444
not m.isOverridable() and
@@ -35,13 +55,29 @@ predicate conditionCheckMethod(Method m, boolean checkTrue) {
3555
ifstmt.getThen().(SingletonBlock).getStmt() instanceof ThrowStmt
3656
)
3757
)
58+
}
59+
60+
/**
61+
* Holds if `m` is a non-overridable method that checks that its zero-indexed `argument`
62+
* is equal to `checkTrue` and throws otherwise.
63+
*/
64+
predicate conditionCheckMethod(Method m, int argument, boolean checkTrue) {
65+
conditionCheckMethod(m, checkTrue) and argument = 0
66+
or
67+
m.getDeclaringType().hasQualifiedName(["org.junit", "org.testng"], "Assert") and
68+
m.getParameter(argument).getType() instanceof BooleanType and
69+
(
70+
checkTrue = true and m.hasName("assertTrue")
71+
or
72+
checkTrue = false and m.hasName("assertFalse")
73+
)
3874
or
39-
exists(Parameter p, MethodAccess ma, boolean ct, Expr arg |
40-
p = m.getParameter(0) and
75+
exists(Parameter p, MethodAccess ma, int argIndex, boolean ct, Expr arg |
76+
p = m.getParameter(argument) and
4177
not m.isOverridable() and
4278
m.getBody().getStmt(0).(ExprStmt).getExpr() = ma and
43-
conditionCheck(ma, ct) and
44-
ma.getArgument(0) = arg and
79+
conditionCheck(ma, argIndex, ct) and
80+
ma.getArgument(argIndex) = arg and
4581
(
4682
arg.(LogNotExpr).getExpr().(VarAccess).getVariable() = p and
4783
checkTrue = ct.booleanNot()
@@ -58,3 +94,11 @@ predicate conditionCheckMethod(Method m, boolean checkTrue) {
5894
predicate conditionCheck(MethodAccess ma, boolean checkTrue) {
5995
conditionCheckMethod(ma.getMethod().getSourceDeclaration(), checkTrue)
6096
}
97+
98+
/**
99+
* Holds if `ma` is an access to a non-overridable method that checks that its
100+
* zero-indexed `argument` is equal to `checkTrue` and throws otherwise.
101+
*/
102+
predicate conditionCheck(MethodAccess ma, int argument, boolean checkTrue) {
103+
conditionCheckMethod(ma.getMethod().getSourceDeclaration(), argument, checkTrue)
104+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import org.junit.Assert;
2+
import org.junit.jupiter.api.Assertions;
3+
4+
public class Preconditions {
5+
public static void guarded() {}
6+
7+
void test1() {
8+
Assert.assertTrue(true);
9+
guarded();
10+
}
11+
12+
void test2() {
13+
Assert.assertTrue(false);
14+
guarded();
15+
}
16+
17+
void test3() {
18+
Assert.assertFalse(false);
19+
guarded();
20+
}
21+
22+
void test4() {
23+
Assert.assertFalse(true);
24+
guarded();
25+
}
26+
27+
void test5() {
28+
Assert.assertTrue("Reason", true);
29+
guarded();
30+
}
31+
32+
void test6() {
33+
Assert.assertTrue("Reason", false);
34+
guarded();
35+
}
36+
37+
void test7() {
38+
Assert.assertFalse("Reason", false);
39+
guarded();
40+
}
41+
42+
void test8() {
43+
Assert.assertFalse("Reason", true);
44+
guarded();
45+
}
46+
47+
void test9() {
48+
r(true);
49+
guarded();
50+
}
51+
52+
static void r(boolean b) {
53+
Assert.assertTrue("Unified Reason", b);
54+
}
55+
56+
void test10() {
57+
Assertions.assertTrue(true);
58+
guarded();
59+
}
60+
61+
void test11() {
62+
Assertions.assertTrue(false);
63+
guarded();
64+
}
65+
66+
void test12() {
67+
Assertions.assertFalse(false);
68+
guarded();
69+
}
70+
71+
void test13() {
72+
Assertions.assertFalse(true);
73+
guarded();
74+
}
75+
76+
void test14() {
77+
Assertions.assertTrue(true, "Reason");
78+
guarded();
79+
}
80+
81+
void test15() {
82+
Assertions.assertTrue(false, "Reason");
83+
guarded();
84+
}
85+
86+
void test16() {
87+
Assertions.assertFalse(false, "Reason");
88+
guarded();
89+
}
90+
91+
void test17() {
92+
Assertions.assertFalse(true, "Reason");
93+
guarded();
94+
}
95+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
| Preconditions.java:8:9:8:31 | assertTrue(...) | true | Preconditions.java:9:9:9:18 | <Expr>; |
2+
| Preconditions.java:13:9:13:32 | assertTrue(...) | true | Preconditions.java:14:9:14:18 | <Expr>; |
3+
| Preconditions.java:18:9:18:33 | assertFalse(...) | false | Preconditions.java:19:9:19:18 | <Expr>; |
4+
| Preconditions.java:23:9:23:32 | assertFalse(...) | false | Preconditions.java:24:9:24:18 | <Expr>; |
5+
| Preconditions.java:48:9:48:15 | r(...) | true | Preconditions.java:49:9:49:18 | <Expr>; |
6+
| Preconditions.java:57:9:57:35 | assertTrue(...) | true | Preconditions.java:58:9:58:18 | <Expr>; |
7+
| Preconditions.java:62:9:62:36 | assertTrue(...) | true | Preconditions.java:63:9:63:18 | <Expr>; |
8+
| Preconditions.java:67:9:67:37 | assertFalse(...) | false | Preconditions.java:68:9:68:18 | <Expr>; |
9+
| Preconditions.java:72:9:72:36 | assertFalse(...) | false | Preconditions.java:73:9:73:18 | <Expr>; |
10+
| Preconditions.java:77:9:77:45 | assertTrue(...) | true | Preconditions.java:78:9:78:18 | <Expr>; |
11+
| Preconditions.java:82:9:82:46 | assertTrue(...) | true | Preconditions.java:83:9:83:18 | <Expr>; |
12+
| Preconditions.java:87:9:87:47 | assertFalse(...) | false | Preconditions.java:88:9:88:18 | <Expr>; |
13+
| Preconditions.java:92:9:92:46 | assertFalse(...) | false | Preconditions.java:93:9:93:18 | <Expr>; |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import java
2+
import semmle.code.java.controlflow.Guards
3+
4+
from Guard g, BasicBlock bb, boolean branch
5+
where
6+
g.controls(bb, branch) and
7+
g.getEnclosingCallable().getDeclaringType().hasName("Preconditions")
8+
select g, branch, bb
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/junit-4.11/:${testdir}/../../stubs/junit-jupiter-api-5.2.0/

java/ql/test/stubs/junit-jupiter-api-5.2.0/org/junit/jupiter/api/Assertions.java

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)