Skip to content

Commit 9918f1b

Browse files
committed
Java: Improve NullGuards.clearlyNotNullExpr()
1 parent a7030c7 commit 9918f1b

File tree

5 files changed

+148
-5
lines changed

5 files changed

+148
-5
lines changed

java/ql/src/semmle/code/java/dataflow/NullGuards.qll

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,32 @@ Expr clearlyNotNullExpr(Expr reason) {
4444
or
4545
result instanceof ArrayCreationExpr and reason = result
4646
or
47+
result instanceof FunctionalExpr and reason = result
48+
or
4749
result instanceof TypeLiteral and reason = result
4850
or
4951
result instanceof ThisAccess and reason = result
5052
or
5153
result instanceof StringLiteral and reason = result
5254
or
55+
// Add and AssignAdd performing String concatenation never have null result
5356
result instanceof AddExpr and result.getType() instanceof TypeString and reason = result
5457
or
58+
result instanceof AssignAddExpr and result.getType() instanceof TypeString and reason = result
59+
or
5560
exists(Field f |
5661
result = f.getAnAccess() and
57-
(f.hasName("TRUE") or f.hasName("FALSE")) and
62+
f.hasName(["TRUE", "FALSE"]) and
5863
f.getDeclaringType().hasQualifiedName("java.lang", "Boolean") and
5964
reason = result
6065
)
6166
or
67+
result.getType() instanceof PrimitiveType and not result instanceof TypeAccess and reason = result
68+
or
69+
// Ignore RValue because that is covered by SsaVariable check below and correctly
70+
// reports the assigned value as 'reason'
71+
result instanceof CompileTimeConstantExpr and not result instanceof RValue and reason = result
72+
or
6273
result.(CastExpr).getExpr() = clearlyNotNullExpr(reason)
6374
or
6475
result.(AssignExpr).getSource() = clearlyNotNullExpr(reason)
@@ -70,6 +81,12 @@ Expr clearlyNotNullExpr(Expr reason) {
7081
(reason = r1 or reason = r2)
7182
)
7283
or
84+
exists(SwitchExpr s |
85+
s = result and
86+
s.getAResult() = clearlyNotNullExpr(reason) and
87+
forall(Expr resultExpr | resultExpr = s.getAResult() | resultExpr = clearlyNotNullExpr())
88+
)
89+
or
7390
exists(SsaVariable v, boolean branch, RValue rval, Guard guard |
7491
guard = directNullGuard(v, branch, false) and
7592
guard.controls(rval.getBasicBlock(), branch) and
@@ -104,10 +121,13 @@ predicate clearlyNotNull(SsaVariable v, Expr reason) {
104121
clearlyNotNull(captured, reason)
105122
)
106123
or
107-
exists(Field f |
108-
v.getSourceVariable().getVariable() = f and
109-
f.isFinal() and
110-
f.getInitializer() = clearlyNotNullExpr(reason)
124+
exists(Variable var |
125+
v.getSourceVariable().getVariable() = var and
126+
var.isFinal() and
127+
var.getAnAssignedValue() = clearlyNotNullExpr(reason) and
128+
forall(Expr assignedValue | assignedValue = var.getAnAssignedValue() |
129+
assignedValue = clearlyNotNullExpr()
130+
)
111131
)
112132
}
113133

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
| Test.java:12:13:12:24 | new Object(...) | Test.java:12:13:12:24 | new Object(...) |
2+
| Test.java:13:21:13:21 | 0 | Test.java:13:21:13:21 | 0 |
3+
| Test.java:14:13:14:16 | this | Test.java:14:13:14:16 | this |
4+
| Test.java:15:13:15:14 | "" | Test.java:15:13:15:14 | "" |
5+
| Test.java:17:13:17:24 | Boolean.TRUE | Test.java:17:13:17:24 | Boolean.TRUE |
6+
| Test.java:18:13:18:25 | Boolean.FALSE | Test.java:18:13:18:25 | Boolean.FALSE |
7+
| Test.java:20:13:20:13 | 1 | Test.java:20:13:20:13 | 1 |
8+
| Test.java:21:13:21:21 | Float.NaN | Test.java:21:13:21:21 | Float.NaN |
9+
| Test.java:22:13:22:20 | constant | Test.java:3:29:3:30 | "" |
10+
| Test.java:23:13:23:23 | (...)... | Test.java:23:22:23:23 | "" |
11+
| Test.java:24:13:24:21 | ...=... | Test.java:24:20:24:21 | "" |
12+
| Test.java:26:13:26:54 | ...?...:... | Test.java:26:38:26:39 | "" |
13+
| Test.java:26:13:26:54 | ...?...:... | Test.java:26:43:26:54 | new Object(...) |
14+
| Test.java:27:13:27:21 | switch (...) | Test.java:3:29:3:30 | "" |
15+
| Test.java:27:13:27:21 | switch (...) | Test.java:28:27:28:28 | "" |
16+
| Test.java:34:13:34:25 | ...::... | Test.java:34:13:34:25 | ...::... |
17+
| Test.java:35:13:35:27 | ...->... | Test.java:35:13:35:27 | ...->... |
18+
| Test.java:41:13:41:18 | ... + ... | Test.java:41:13:41:18 | ... + ... |
19+
| Test.java:42:13:42:20 | ... + ... | Test.java:42:13:42:20 | ... + ... |
20+
| Test.java:43:13:43:19 | ...+=... | Test.java:43:13:43:19 | ...+=... |
21+
| Test.java:44:13:44:32 | ... + ... | Test.java:44:13:44:32 | ... + ... |
22+
| Test.java:51:17:51:18 | n1 | Test.java:49:13:49:22 | ... != ... |
23+
| Test.java:59:13:59:14 | n2 | Test.java:57:14:57:15 | "" |
24+
| Test.java:70:13:70:14 | n3 | Test.java:65:18:65:19 | "" |
25+
| Test.java:70:13:70:14 | n3 | Test.java:67:18:67:18 | 1 |
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import java
2+
import semmle.code.java.dataflow.NullGuards
3+
4+
from Expr notNull, Expr reason
5+
where
6+
notNull = clearlyNotNullExpr(reason) and
7+
// Restrict to ArrayInit to make results easier to read
8+
notNull.getParent() instanceof ArrayInit
9+
select notNull, reason
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
class NonNullTest {
2+
final String constantNull = null;
3+
final String constant = "";
4+
5+
Object getMaybeNull() {
6+
return null;
7+
}
8+
9+
void notNull() {
10+
Object temp;
11+
Object[] r = {
12+
new Object(),
13+
new int[0],
14+
this,
15+
"",
16+
17+
Boolean.TRUE,
18+
Boolean.FALSE,
19+
20+
1,
21+
Float.NaN,
22+
constant,
23+
(Object) "",
24+
temp = "",
25+
26+
getMaybeNull() == null ? "" : new Object(),
27+
switch(1) {
28+
case 1 -> "";
29+
default -> constant;
30+
}
31+
};
32+
33+
Runnable[] functional = {
34+
this::notNull,
35+
() -> notNull()
36+
};
37+
38+
String s = null;
39+
String s2 = null;
40+
r = new Object[] {
41+
s + s2,
42+
null + s,
43+
s += s2,
44+
(String) null + null
45+
};
46+
47+
Object n1 = getMaybeNull();
48+
// Guarded by null check
49+
if (n1 != null) {
50+
r = new Object[] {
51+
n1
52+
};
53+
}
54+
55+
Object n2 = getMaybeNull();
56+
// Assigned a non-null value
57+
n2 = "";
58+
r = new Object[] {
59+
n2
60+
};
61+
62+
// final variable for which all assigned values are non-null
63+
final Object n3;
64+
if (getMaybeNull() == null) {
65+
n3 = "";
66+
} else {
67+
n3 = 1;
68+
}
69+
r = new Object[] {
70+
n3
71+
};
72+
}
73+
74+
void maybeNull(boolean b, int i) {
75+
Object temp;
76+
Object[] r = {
77+
constantNull,
78+
(Object) null,
79+
temp = (String) null,
80+
b ? "" : null,
81+
switch(i) {
82+
case 1 -> "";
83+
default -> null;
84+
},
85+
null
86+
};
87+
}
88+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -source 15 -target 15

0 commit comments

Comments
 (0)