Skip to content

Commit 77b1721

Browse files
committed
Move TypeTestGuard's logic into Guard.appliesTypeTest
1 parent b33dc38 commit 77b1721

File tree

3 files changed

+38
-59
lines changed

3 files changed

+38
-59
lines changed

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

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,39 @@ class Guard extends ExprParent {
164164
)
165165
}
166166

167+
/**
168+
* Holds if this guard tests whether `testedExpr` has type `testedType`.
169+
*
170+
* `restricted` is true if the test applies additional restrictions on top of just `testedType`, and so
171+
* this guard failing does not guarantee `testedExpr` is *not* a `testedType`-- for example,
172+
* matching `record R(Object o)` with `case R(String s)` is a guard with an additional restriction on the
173+
* type of field `o`, so the guard passing guarantees `testedExpr` is an `R`, but it failing does not
174+
* guarantee `testedExpr` is not an `R`.
175+
*/
176+
predicate appliesTypeTest(Expr testedExpr, Type testedType, boolean restricted) {
177+
(
178+
exists(InstanceOfExpr ioe | this = ioe |
179+
testedExpr = ioe.getExpr() and
180+
testedType = ioe.getSyntacticCheckedType()
181+
)
182+
or
183+
exists(PatternCase pc | this = pc |
184+
pc.getSelectorExpr() = testedExpr and
185+
testedType = pc.getPattern().getType()
186+
)
187+
) and
188+
(
189+
if
190+
exists(RecordPatternExpr rpe |
191+
rpe = [this.(InstanceOfExpr).getPattern(), this.(PatternCase).getPattern()]
192+
|
193+
not rpe.isUnrestricted()
194+
)
195+
then restricted = true
196+
else restricted = false
197+
)
198+
}
199+
167200
/**
168201
* Holds if the evaluation of this guard to `branch` corresponds to the edge
169202
* from `bb1` to `bb2`.
@@ -223,60 +256,6 @@ class Guard extends ExprParent {
223256
}
224257
}
225258

226-
/**
227-
* A `Guard` that tests an expression's type -- that is, an `instanceof T` or a
228-
* `case T varname` pattern case.
229-
*/
230-
class TypeTestGuard extends Guard {
231-
Expr testedExpr;
232-
Type testedType;
233-
234-
TypeTestGuard() {
235-
exists(InstanceOfExpr ioe | this = ioe |
236-
testedExpr = ioe.getExpr() and
237-
testedType = ioe.getSyntacticCheckedType()
238-
)
239-
or
240-
exists(PatternCase pc | this = pc |
241-
pc.getSelectorExpr() = testedExpr and
242-
testedType = pc.getPattern().getType()
243-
)
244-
}
245-
246-
/**
247-
* Gets the record pattern this type test binds to, if any.
248-
*/
249-
PatternExpr getPattern() {
250-
result = this.(InstanceOfExpr).getPattern()
251-
or
252-
result = this.(PatternCase).getPattern()
253-
}
254-
255-
/**
256-
* Holds if this guard tests whether `e` has type `t` on `testedBranch`.
257-
*
258-
* Note that record patterns that make at least one tighter restriction than the record's definition
259-
* (e.g. matching `record R(Object)` with `case R(String)`) means this only guarantees the tested type
260-
* on the true branch (i.e., entering such a case guarantees `testedExpr` is a `testedType`, but failing
261-
* the type test could mean a nested record or binding pattern didn't match but `testedExpr` is still
262-
* of type `testedType`.)
263-
*/
264-
predicate appliesTypeTest(Expr e, Type t, boolean testedBranch) {
265-
e = testedExpr and
266-
t = testedType and
267-
(
268-
testedBranch = true
269-
or
270-
testedBranch = false and
271-
(
272-
this.getPattern().asRecordPattern().isUnrestricted()
273-
or
274-
not this.getPattern() instanceof RecordPatternExpr
275-
)
276-
)
277-
}
278-
}
279-
280259
private predicate switchCaseControls(SwitchCase sc, BasicBlock bb) {
281260
exists(BasicBlock caseblock |
282261
caseblock.getFirstNode() = sc.getControlFlowNode() and

java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ private predicate downcastSuccessor(VarAccess va, RefType t) {
417417
* Holds if `va` is an access to a value that is guarded by `instanceof t` or `case e t`.
418418
*/
419419
private predicate typeTestGuarded(VarAccess va, RefType t) {
420-
exists(TypeTestGuard typeTest, BaseSsaVariable v |
421-
typeTest.appliesTypeTest(v.getAUse(), t, true) and
420+
exists(Guard typeTest, BaseSsaVariable v |
421+
typeTest.appliesTypeTest(v.getAUse(), t, _) and
422422
va = v.getAUse() and
423423
guardControls_v1(typeTest, va.getBasicBlock(), true)
424424
)
@@ -428,8 +428,8 @@ private predicate typeTestGuarded(VarAccess va, RefType t) {
428428
* Holds if `aa` is an access to a value that is guarded by `instanceof t` or `case e t`.
429429
*/
430430
predicate arrayTypeTestGuarded(ArrayAccess aa, RefType t) {
431-
exists(TypeTestGuard typeTest, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
432-
typeTest.appliesTypeTest(aa1, t, true) and
431+
exists(Guard typeTest, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
432+
typeTest.appliesTypeTest(aa1, t, _) and
433433
aa1.getArray() = v1.getAUse() and
434434
aa1.getIndexExpr() = v2.getAUse() and
435435
aa.getArray() = v1.getAUse() and

java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ private module Dispatch {
194194
*/
195195
private predicate impossibleDispatchTarget(MethodCall source, Method tgt) {
196196
tgt = viableImpl_v1_cand(source) and
197-
exists(TypeTestGuard typeTest, BaseSsaVariable v, Expr q, RefType t |
197+
exists(Guard typeTest, BaseSsaVariable v, Expr q, RefType t |
198198
source.getQualifier() = q and
199199
v.getAUse() = q and
200200
typeTest.appliesTypeTest(v.getAUse(), t, false) and

0 commit comments

Comments
 (0)