mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge pull request #1294 from yh-semmle/java12-ql
Java: add Java 12 support
This commit is contained in:
@@ -17,7 +17,7 @@ import semmle.code.java.JDK
|
||||
class CheckedCast extends CastExpr {
|
||||
CheckedCast() {
|
||||
exists(TryStmt try, RefType cce |
|
||||
this.getEnclosingStmt().getParent+() = try and
|
||||
this.getEnclosingStmt().getEnclosingStmt+() = try and
|
||||
try.getACatchClause().getVariable().getType() = cce and
|
||||
cce.getQualifiedName() = "java.lang.ClassCastException"
|
||||
)
|
||||
|
||||
@@ -69,8 +69,8 @@ class DangerStmt extends Stmt {
|
||||
|
||||
from WhileStmt s, DangerStmt d
|
||||
where
|
||||
d.getParent+() = s and
|
||||
d.getEnclosingStmt+() = s and
|
||||
not exists(MethodAccess call | callsCommunicationMethod(call.getMethod()) |
|
||||
call.getEnclosingStmt().getParent*() = s
|
||||
call.getEnclosingStmt().getEnclosingStmt*() = s
|
||||
)
|
||||
select d, "Prefer wait/notify or java.util.concurrent to communicate between threads."
|
||||
|
||||
@@ -33,7 +33,7 @@ where
|
||||
1 = strictcount(FieldAccess fa |
|
||||
fa.getField() = f and
|
||||
fa.getEnclosingCallable() = sync.getEnclosingCallable() and
|
||||
not fa.getEnclosingStmt().getParent*() = sync.getBlock()
|
||||
not fa.getEnclosingStmt().getEnclosingStmt*() = sync.getBlock()
|
||||
)
|
||||
)
|
||||
select sync, "Double-checked locking on the non-volatile field $@ is not thread-safe.", f,
|
||||
|
||||
@@ -35,8 +35,8 @@ private Expr getANullCheck(Field f) {
|
||||
* as they are always safe to initialize with double-checked locking.
|
||||
*/
|
||||
predicate doubleCheckedLocking(IfStmt if1, IfStmt if2, SynchronizedStmt sync, Field f) {
|
||||
if1.getThen() = sync.getParent*() and
|
||||
sync.getBlock() = if2.getParent*() and
|
||||
if1.getThen() = sync.getEnclosingStmt*() and
|
||||
sync.getBlock() = if2.getEnclosingStmt*() and
|
||||
if1.getCondition() = getANullCheck(f) and
|
||||
if2.getCondition() = getANullCheck(f)
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ class SideEffect extends Expr {
|
||||
from IfStmt if1, IfStmt if2, SynchronizedStmt sync, Field f, AssignExpr a, SideEffect se
|
||||
where
|
||||
doubleCheckedLocking(if1, if2, sync, f) and
|
||||
a.getEnclosingStmt().getParent*() = if2.getThen() and
|
||||
se.getEnclosingStmt().getParent*() = sync.getBlock() and
|
||||
a.getEnclosingStmt().getEnclosingStmt*() = if2.getThen() and
|
||||
se.getEnclosingStmt().getEnclosingStmt*() = sync.getBlock() and
|
||||
a.(ControlFlowNode).getASuccessor+() = se and
|
||||
a.getDest().(FieldAccess).getField() = f
|
||||
select a,
|
||||
|
||||
@@ -23,6 +23,6 @@ from SynchronizedStmt s, Field f, Assignment a
|
||||
where
|
||||
synchField(s) = f and
|
||||
assignmentToField(a) = f and
|
||||
a.getEnclosingStmt().getParent*() = s
|
||||
a.getEnclosingStmt().getEnclosingStmt*() = s
|
||||
select a, "Synchronization on field $@ in futile attempt to guard that field.", f,
|
||||
f.getDeclaringType().getName() + "." + f.getName()
|
||||
|
||||
@@ -24,7 +24,7 @@ predicate withinInitializer(Expr e) {
|
||||
}
|
||||
|
||||
predicate locallySynchronized(MethodAccess ma) {
|
||||
ma.getEnclosingStmt().getParent+() instanceof SynchronizedStmt
|
||||
ma.getEnclosingStmt().getEnclosingStmt+() instanceof SynchronizedStmt
|
||||
}
|
||||
|
||||
predicate hasUnsynchronizedCall(Method m) {
|
||||
@@ -41,7 +41,7 @@ predicate hasUnsynchronizedCall(Method m) {
|
||||
|
||||
predicate withinLocalSynchronization(Expr e) {
|
||||
e.getEnclosingCallable().isSynchronized() or
|
||||
e.getEnclosingStmt().getParent+() instanceof SynchronizedStmt
|
||||
e.getEnclosingStmt().getEnclosingStmt+() instanceof SynchronizedStmt
|
||||
}
|
||||
|
||||
class MyField extends Field {
|
||||
|
||||
@@ -94,14 +94,14 @@ where
|
||||
not method instanceof StaticInitializer and
|
||||
// There must be an unsynchronized read.
|
||||
exists(IfStmt unsyncNullCheck | unsyncNullCheck = init.getAnEnclosingNullCheck() |
|
||||
not unsyncNullCheck.getParent+() instanceof ValidSynchStmt
|
||||
not unsyncNullCheck.getEnclosingStmt+() instanceof ValidSynchStmt
|
||||
) and
|
||||
if i.getParent+() instanceof ValidSynchStmt
|
||||
if i.getEnclosingStmt+() instanceof ValidSynchStmt
|
||||
then (
|
||||
not init.getField().isVolatile() and
|
||||
message = "The field must be volatile."
|
||||
) else (
|
||||
if i.getParent+() instanceof SynchronizedStmt
|
||||
if i.getEnclosingStmt+() instanceof SynchronizedStmt
|
||||
then message = "Bad synchronization."
|
||||
else message = "Missing synchronization."
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ where
|
||||
sleep.hasName("sleep") and
|
||||
sleep.getDeclaringType().hasQualifiedName("java.lang", "Thread") and
|
||||
(
|
||||
ma.getEnclosingStmt().getParent*() instanceof SynchronizedStmt or
|
||||
ma.getEnclosingStmt().getEnclosingStmt*() instanceof SynchronizedStmt or
|
||||
ma.getEnclosingCallable().isSynchronized()
|
||||
)
|
||||
select ma, "sleep() with lock held."
|
||||
|
||||
@@ -23,5 +23,5 @@ class WaitMethod extends Method {
|
||||
from MethodAccess ma
|
||||
where
|
||||
ma.getMethod() instanceof WaitMethod and
|
||||
not exists(LoopStmt s | ma.getEnclosingStmt().getParent*() = s)
|
||||
not exists(LoopStmt s | ma.getEnclosingStmt().getEnclosingStmt*() = s)
|
||||
select ma, "To avoid spurious wake-ups, 'wait' should only be called inside a loop."
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
|
||||
import java
|
||||
|
||||
/** A `synchronized` method or statement. */
|
||||
class Synched extends StmtParent {
|
||||
/** A `synchronized` method body or statement. */
|
||||
class Synched extends Stmt {
|
||||
Synched() {
|
||||
this.(Method).isSynchronized() or
|
||||
this.getParent().(Method).isSynchronized() or
|
||||
this instanceof SynchronizedStmt
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,6 @@ from MethodAccess ma, SynchronizedStmt synch
|
||||
where
|
||||
ma.getMethod().hasName("wait") and
|
||||
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "Object") and
|
||||
ma.getEnclosingStmt().getParent*() = synch and
|
||||
synch.getParent+() instanceof Synched
|
||||
ma.getEnclosingStmt().getEnclosingStmt*() = synch and
|
||||
synch.getEnclosingStmt+() instanceof Synched
|
||||
select ma, "wait() with two locks held."
|
||||
|
||||
@@ -17,7 +17,7 @@ from ForStmt inner, Variable iteration, ForStmt outer
|
||||
where
|
||||
iteration = inner.getAnIterationVariable() and
|
||||
iteration = outer.getAnIterationVariable() and
|
||||
inner.getParent+() = outer and
|
||||
inner.getEnclosingStmt+() = outer and
|
||||
inner.getBasicBlock().getABBSuccessor+() = outer.getCondition().getBasicBlock()
|
||||
select inner.getCondition(), "Nested for statement uses loop variable $@ of enclosing $@.",
|
||||
iteration, iteration.getName(), outer, "for statement"
|
||||
|
||||
@@ -19,8 +19,8 @@ import java
|
||||
* and are therefore not propagated to the outer try block `t`.
|
||||
*/
|
||||
private predicate caughtInside(TryStmt t, Stmt s, RefType rt) {
|
||||
exists(TryStmt innerTry | innerTry.getParent+() = t.getBlock() |
|
||||
s.getParent+() = innerTry.getBlock() and
|
||||
exists(TryStmt innerTry | innerTry.getEnclosingStmt+() = t.getBlock() |
|
||||
s.getEnclosingStmt+() = innerTry.getBlock() and
|
||||
caughtType(innerTry, _).hasSubtype*(rt)
|
||||
)
|
||||
}
|
||||
@@ -43,7 +43,7 @@ private RefType getAThrownExceptionType(TryStmt t) {
|
||||
)
|
||||
or
|
||||
exists(Call call, Exception e |
|
||||
t.getBlock() = call.getEnclosingStmt().getParent*() or
|
||||
t.getBlock() = call.getEnclosingStmt().getEnclosingStmt*() or
|
||||
t.getAResourceDecl() = call.getEnclosingStmt()
|
||||
|
|
||||
(
|
||||
@@ -55,7 +55,7 @@ private RefType getAThrownExceptionType(TryStmt t) {
|
||||
)
|
||||
or
|
||||
exists(ThrowStmt ts |
|
||||
t.getBlock() = ts.getParent*() and
|
||||
t.getBlock() = ts.getEnclosingStmt*() and
|
||||
not caughtInside(t, ts, ts.getExpr().getType()) and
|
||||
result = ts.getExpr().getType()
|
||||
)
|
||||
|
||||
@@ -29,10 +29,10 @@ predicate loopWhileTrue(LoopStmt loop) {
|
||||
* is worth flagging even if it has a reachable exceptional loop exit.
|
||||
*/
|
||||
predicate loopExit(LoopStmt loop, Stmt exit) {
|
||||
exit.getParent*() = loop.getBody() and
|
||||
exit.getEnclosingStmt*() = loop.getBody() and
|
||||
(
|
||||
exit instanceof ReturnStmt or
|
||||
exit.(BreakStmt).(JumpStmt).getTarget() = loop.getParent*()
|
||||
exit.(BreakStmt).(JumpStmt).getTarget() = loop.getEnclosingStmt*()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ predicate loopExit(LoopStmt loop, Stmt exit) {
|
||||
predicate loopExitGuard(LoopStmt loop, Expr cond) {
|
||||
exists(ConditionBlock cb, boolean branch |
|
||||
cond = cb.getCondition() and
|
||||
cond.getEnclosingStmt().getParent*() = loop.getBody() and
|
||||
cond.getEnclosingStmt().getEnclosingStmt*() = loop.getBody() and
|
||||
forex(Stmt exit | loopExit(loop, exit) | cb.controls(exit.getBasicBlock(), branch))
|
||||
)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ predicate mainLoopCondition(LoopStmt loop, Expr cond) {
|
||||
then loopReentry = loop.(ForStmt).getUpdate(0)
|
||||
else loopReentry = cond
|
||||
|
|
||||
last.getEnclosingStmt().getParent*() = loop.getBody() and
|
||||
last.getEnclosingStmt().getEnclosingStmt*() = loop.getBody() and
|
||||
last.getASuccessor().(Expr).getParent*() = loopReentry
|
||||
)
|
||||
}
|
||||
@@ -74,7 +74,7 @@ where
|
||||
) and
|
||||
// None of the ssa variables in `cond` are updated inside the loop.
|
||||
forex(SsaVariable ssa, RValue use | ssa.getAUse() = use and use.getParent*() = cond |
|
||||
not ssa.getCFGNode().getEnclosingStmt().getParent*() = loop or
|
||||
not ssa.getCFGNode().getEnclosingStmt().getEnclosingStmt*() = loop or
|
||||
ssa.getCFGNode().(Expr).getParent*() = loop.(ForStmt).getAnInit()
|
||||
) and
|
||||
// And `cond` does not use method calls, field reads, or array reads.
|
||||
|
||||
@@ -48,7 +48,7 @@ predicate useAndDef(Assignment a, Variable v) {
|
||||
predicate declaredInLoop(LocalVariableDecl v, LoopStmt loop) {
|
||||
exists(LocalVariableDeclExpr e |
|
||||
e.getVariable() = v and
|
||||
e.getEnclosingStmt().getParent*() = loop.getBody()
|
||||
e.getEnclosingStmt().getEnclosingStmt*() = loop.getBody()
|
||||
)
|
||||
or
|
||||
exists(EnhancedForStmt for | for = loop | for.getVariable().getVariable() = v)
|
||||
@@ -57,5 +57,5 @@ predicate declaredInLoop(LocalVariableDecl v, LoopStmt loop) {
|
||||
from Assignment a, Variable v
|
||||
where
|
||||
useAndDef(a, v) and
|
||||
exists(LoopStmt loop | a.getEnclosingStmt().getParent*() = loop | not declaredInLoop(v, loop))
|
||||
exists(LoopStmt loop | a.getEnclosingStmt().getEnclosingStmt*() = loop | not declaredInLoop(v, loop))
|
||||
select a, "The string " + v.getName() + " is built-up in a loop: use string buffer."
|
||||
|
||||
@@ -46,7 +46,9 @@ class Synched extends Top {
|
||||
or
|
||||
result = this.(SynchronizedStmt).getAChild+()
|
||||
or
|
||||
exists(MethodAccess ma | ma = result | ma.getEnclosingStmt().getParent*() = this)
|
||||
exists(MethodAccess ma | ma = result |
|
||||
ma.getEnclosingStmt().getEnclosingStmt*() = this or ma.getEnclosingCallable() = this
|
||||
)
|
||||
}
|
||||
|
||||
/** The variable on which synchronization is performed, provided this element is a `SynchronizedStmt`. */
|
||||
|
||||
@@ -22,16 +22,16 @@ predicate loopCondition(LoopStmt loop, Expr cond, boolean polarity) {
|
||||
polarity = true and cond = loop.getCondition()
|
||||
or
|
||||
exists(IfStmt ifstmt, Stmt exit |
|
||||
ifstmt.getParent*() = loop.getBody() and
|
||||
ifstmt.getEnclosingStmt*() = loop.getBody() and
|
||||
ifstmt.getCondition() = cond and
|
||||
(
|
||||
exit.(BreakStmt).(JumpStmt).getTarget() = loop or
|
||||
exit.(ReturnStmt).getParent*() = loop.getBody()
|
||||
exit.(ReturnStmt).getEnclosingStmt*() = loop.getBody()
|
||||
) and
|
||||
(
|
||||
polarity = false and exit.getParent*() = ifstmt.getThen()
|
||||
polarity = false and exit.getEnclosingStmt*() = ifstmt.getThen()
|
||||
or
|
||||
polarity = true and exit.getParent*() = ifstmt.getElse()
|
||||
polarity = true and exit.getEnclosingStmt*() = ifstmt.getElse()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,19 +22,19 @@ private predicate relevantTypeNames(string typeName, string message) {
|
||||
|
||||
private Type getAThrownExceptionType(TryStmt t) {
|
||||
exists(MethodAccess ma, Exception e |
|
||||
t.getBlock() = ma.getEnclosingStmt().getParent*() and
|
||||
t.getBlock() = ma.getEnclosingStmt().getEnclosingStmt*() and
|
||||
ma.getMethod().getAnException() = e and
|
||||
result = e.getType()
|
||||
)
|
||||
or
|
||||
exists(ClassInstanceExpr cie, Exception e |
|
||||
t.getBlock() = cie.getEnclosingStmt().getParent*() and
|
||||
t.getBlock() = cie.getEnclosingStmt().getEnclosingStmt*() and
|
||||
cie.getConstructor().getAnException() = e and
|
||||
result = e.getType()
|
||||
)
|
||||
or
|
||||
exists(ThrowStmt ts |
|
||||
t.getBlock() = ts.getParent*() and
|
||||
t.getBlock() = ts.getEnclosingStmt*() and
|
||||
result = ts.getExpr().getType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ from Expr e
|
||||
where
|
||||
throwsNFE(e) and
|
||||
not exists(TryStmt t |
|
||||
t.getBlock() = e.getEnclosingStmt().getParent*() and
|
||||
t.getBlock() = e.getEnclosingStmt().getEnclosingStmt*() and
|
||||
catchesNFE(t)
|
||||
) and
|
||||
not exists(Callable c |
|
||||
|
||||
@@ -28,7 +28,7 @@ predicate guardedByInstanceOf(VarAccess e, RefType t) {
|
||||
// The expression appears in one of the branches.
|
||||
// (We do not verify here whether the guard is correctly implemented.)
|
||||
exists(Stmt branch | branch = s.getThen() or branch = s.getElse() |
|
||||
branch = e.getEnclosingStmt().getParent+()
|
||||
branch = e.getEnclosingStmt().getEnclosingStmt+()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ stmts(
|
||||
int bodydecl: @callable ref
|
||||
);
|
||||
|
||||
@stmtparent = @callable | @stmt;
|
||||
@stmtparent = @callable | @stmt | @switchexpr;
|
||||
|
||||
case @stmt.kind of
|
||||
0 = @block
|
||||
@@ -491,6 +491,7 @@ case @expr.kind of
|
||||
| 70 = @annotatedtypeaccess
|
||||
| 71 = @typeannotation
|
||||
| 72 = @intersectiontypeaccess
|
||||
| 73 = @switchexpr
|
||||
;
|
||||
|
||||
@classinstancexpr = @newexpr | @lambdaexpr | @memberref
|
||||
|
||||
@@ -444,6 +444,10 @@
|
||||
<v>277067</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@switchexpr</k>
|
||||
<v>4</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@uniontypeaccess</k>
|
||||
<v>158</v>
|
||||
</e>
|
||||
|
||||
@@ -52,9 +52,13 @@ newtype Completion =
|
||||
(innerValue = true or innerValue = false)
|
||||
} or
|
||||
/**
|
||||
* The expression or statement completes via a `break` statement.
|
||||
* The expression or statement completes via a `break` statement without a value.
|
||||
*/
|
||||
BreakCompletion(MaybeLabel l) or
|
||||
/**
|
||||
* The expression or statement completes via a value `break` statement.
|
||||
*/
|
||||
ValueBreakCompletion(NormalOrBooleanCompletion c) or
|
||||
/**
|
||||
* The expression or statement completes via a `continue` statement.
|
||||
*/
|
||||
@@ -64,6 +68,14 @@ newtype Completion =
|
||||
*/
|
||||
ThrowCompletion(ThrowableType tt)
|
||||
|
||||
class NormalOrBooleanCompletion extends Completion {
|
||||
NormalOrBooleanCompletion() {
|
||||
this instanceof NormalCompletion or this instanceof BooleanCompletion
|
||||
}
|
||||
|
||||
string toString() { result = "completion" }
|
||||
}
|
||||
|
||||
ContinueCompletion anonymousContinueCompletion() { result = ContinueCompletion(NoLabel()) }
|
||||
|
||||
ContinueCompletion labelledContinueCompletion(Label l) { result = ContinueCompletion(JustLabel(l)) }
|
||||
|
||||
@@ -4,7 +4,7 @@ import java
|
||||
* Holds if `e` is synchronized by a local synchronized statement `sync` on the variable `v`.
|
||||
*/
|
||||
predicate locallySynchronizedOn(Expr e, SynchronizedStmt sync, Variable v) {
|
||||
e.getEnclosingStmt().getParent+() = sync and
|
||||
e.getEnclosingStmt().getEnclosingStmt+() = sync and
|
||||
sync.getExpr().(VarAccess).getVariable() = v
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ predicate locallySynchronizedOn(Expr e, SynchronizedStmt sync, Variable v) {
|
||||
* modifier on the enclosing (non-static) method.
|
||||
*/
|
||||
predicate locallySynchronizedOnThis(Expr e, RefType thisType) {
|
||||
exists(SynchronizedStmt sync | e.getEnclosingStmt().getParent+() = sync |
|
||||
exists(SynchronizedStmt sync | e.getEnclosingStmt().getEnclosingStmt+() = sync |
|
||||
sync.getExpr().getProperExpr().(ThisAccess).getType().(RefType).getSourceDeclaration() = thisType
|
||||
)
|
||||
or
|
||||
|
||||
@@ -194,7 +194,7 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private predicate uncheckedExceptionFromFinally(ControlFlowNode n, ThrowableType t) {
|
||||
exists(TryStmt try |
|
||||
n.getEnclosingStmt().getParent+() = try.getBlock() or
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
|
|
||||
exists(try.getFinally()) and
|
||||
@@ -208,7 +208,7 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private predicate uncheckedExceptionFromCatch(ControlFlowNode n, ThrowableType t) {
|
||||
exists(TryStmt try, UncheckedThrowableSuperType caught |
|
||||
n.getEnclosingStmt().getParent+() = try.getBlock() or
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
|
|
||||
t = caught.getAnUncheckedSubtype() and
|
||||
@@ -222,7 +222,7 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private ThrowableType thrownInBody(TryStmt try) {
|
||||
exists(ControlFlowNode n | mayThrow(n, result) |
|
||||
n.getEnclosingStmt().getParent+() = try.getBlock() or
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
)
|
||||
}
|
||||
@@ -298,6 +298,11 @@ private module ControlFlowGraphImpl {
|
||||
inBooleanContext(condexpr)
|
||||
)
|
||||
or
|
||||
exists(SwitchExpr switch |
|
||||
inBooleanContext(switch) and
|
||||
switch.getAResult() = b
|
||||
)
|
||||
or
|
||||
exists(ConditionalStmt condstmt | condstmt.getCondition() = b)
|
||||
}
|
||||
|
||||
@@ -447,7 +452,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
this.(Block).getNumStmt() = 0
|
||||
or
|
||||
this instanceof SwitchCase
|
||||
this instanceof SwitchCase and not this.(SwitchCase).isRule()
|
||||
or
|
||||
this instanceof EmptyStmt
|
||||
or
|
||||
@@ -551,7 +556,7 @@ private module ControlFlowGraphImpl {
|
||||
// somewhere inside this loop; we don't particularly care whether that
|
||||
// `continue` could actually target this loop, we just want to restrict
|
||||
// the size of the predicate
|
||||
exists(ContinueStmt cnt | cnt.getParent+() = loop |
|
||||
exists(ContinueStmt cnt | cnt.getEnclosingStmt+() = loop |
|
||||
completion = anonymousContinueCompletion() or
|
||||
completion = labelledContinueCompletion(getLabel(loop))
|
||||
)
|
||||
@@ -651,8 +656,9 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private predicate last(ControlFlowNode n, ControlFlowNode last, Completion completion) {
|
||||
// Exceptions are propagated from any sub-expression.
|
||||
// As are any break, continue, or return completions.
|
||||
exists(Expr e | e.getParent() = n |
|
||||
last(e, last, completion) and completion = ThrowCompletion(_)
|
||||
last(e, last, completion) and not completion instanceof NormalOrBooleanCompletion
|
||||
)
|
||||
or
|
||||
// If an expression doesn't finish with a throw completion, then it executes normally with
|
||||
@@ -796,6 +802,28 @@ private module ControlFlowGraphImpl {
|
||||
completion = NormalCompletion()
|
||||
)
|
||||
or
|
||||
// handle `switch` expression
|
||||
exists(SwitchExpr switch | switch = n |
|
||||
// value `break` terminates the `switch`
|
||||
last(switch.getAStmt(), last, ValueBreakCompletion(completion))
|
||||
or
|
||||
// any other abnormal completion is propagated
|
||||
last(switch.getAStmt(), last, completion) and
|
||||
not completion instanceof ValueBreakCompletion and
|
||||
completion != NormalCompletion()
|
||||
)
|
||||
or
|
||||
// the last node in a case rule is the last node in the right-hand side
|
||||
last(n.(SwitchCase).getRuleStatement(), last, completion)
|
||||
or
|
||||
// ...and if the rhs is an expression we wrap the completion as a value break
|
||||
exists(Completion caseCompletion |
|
||||
last(n.(SwitchCase).getRuleExpression(), last, caseCompletion) and
|
||||
if caseCompletion instanceof NormalOrBooleanCompletion
|
||||
then completion = ValueBreakCompletion(caseCompletion)
|
||||
else completion = caseCompletion
|
||||
)
|
||||
or
|
||||
// the last statement of a synchronized statement is the last statement of its body
|
||||
last(n.(SynchronizedStmt).getBlock(), last, completion)
|
||||
or
|
||||
@@ -805,13 +833,21 @@ private module ControlFlowGraphImpl {
|
||||
// `throw` statements or throwing calls give rise to ` Throw` completion
|
||||
exists(ThrowableType tt | mayThrow(n, tt) | last = n and completion = ThrowCompletion(tt))
|
||||
or
|
||||
// `break` statements give rise to a `Break` completion
|
||||
exists(BreakStmt break | break = n and last = n |
|
||||
// `break` statements without value give rise to a `Break` completion
|
||||
exists(BreakStmt break | break = n and last = n and not break.hasValue() |
|
||||
completion = labelledBreakCompletion(MkLabel(break.getLabel()))
|
||||
or
|
||||
not exists(break.getLabel()) and completion = anonymousBreakCompletion()
|
||||
)
|
||||
or
|
||||
// value break statements get their completion wrapped as a value break
|
||||
exists(Completion caseCompletion |
|
||||
last(n.(BreakStmt).getValue(), last, caseCompletion) and
|
||||
if caseCompletion instanceof NormalOrBooleanCompletion
|
||||
then completion = ValueBreakCompletion(caseCompletion)
|
||||
else completion = caseCompletion
|
||||
)
|
||||
or
|
||||
// `continue` statements give rise to a `Continue` completion
|
||||
exists(ContinueStmt cont | cont = n and last = n |
|
||||
completion = labelledContinueCompletion(MkLabel(cont.getLabel()))
|
||||
@@ -1055,7 +1091,32 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
)
|
||||
or
|
||||
// No edges in a SwitchCase - the constant expression in a ConstCase isn't included in the CFG.
|
||||
// Switch expressions
|
||||
exists(SwitchExpr switch | completion = NormalCompletion() |
|
||||
// From the entry point control is transferred first to the expression...
|
||||
n = switch and result = first(switch.getExpr())
|
||||
or
|
||||
// ...and then to one of the cases.
|
||||
last(switch.getExpr(), n, completion) and result = first(switch.getACase())
|
||||
or
|
||||
// Statements within a switch body execute sequentially.
|
||||
exists(int i |
|
||||
last(switch.getStmt(i), n, completion) and result = first(switch.getStmt(i + 1))
|
||||
)
|
||||
)
|
||||
or
|
||||
// No edges in a non-rule SwitchCase - the constant expression in a ConstCase isn't included in the CFG.
|
||||
exists(SwitchCase case | completion = NormalCompletion() |
|
||||
n = case and result = first(case.getRuleExpression())
|
||||
or
|
||||
n = case and result = first(case.getRuleStatement())
|
||||
)
|
||||
or
|
||||
// Value break
|
||||
exists(BreakStmt break | completion = NormalCompletion() |
|
||||
n = break and result = first(break.getValue())
|
||||
)
|
||||
or
|
||||
// Synchronized statements execute their expression _before_ synchronization, so the CFG reflects that.
|
||||
exists(SynchronizedStmt synch | completion = NormalCompletion() |
|
||||
last(synch.getExpr(), n, completion) and result = synch
|
||||
|
||||
@@ -1093,6 +1093,46 @@ class ConditionalExpr extends Expr, @conditionalexpr {
|
||||
override string toString() { result = "...?...:..." }
|
||||
}
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* A `switch` expression.
|
||||
*/
|
||||
class SwitchExpr extends Expr, @switchexpr {
|
||||
/** Gets an immediate child statement of this `switch` expression. */
|
||||
Stmt getAStmt() { result.getParent() = this }
|
||||
|
||||
/**
|
||||
* Gets the immediate child statement of this `switch` expression
|
||||
* that occurs at the specified (zero-based) position.
|
||||
*/
|
||||
Stmt getStmt(int index) { result = this.getAStmt() and result.getIndex() = index }
|
||||
|
||||
/**
|
||||
* Gets a case of this `switch` expression,
|
||||
* which may be either a normal `case` or a `default`.
|
||||
*/
|
||||
SwitchCase getACase() { result = getAConstCase() or result = getDefaultCase() }
|
||||
|
||||
/** Gets a (non-default) `case` of this `switch` expression. */
|
||||
ConstCase getAConstCase() { result.getParent() = this }
|
||||
|
||||
/** Gets the `default` case of this switch expression, if any. */
|
||||
DefaultCase getDefaultCase() { result.getParent() = this }
|
||||
|
||||
/** Gets the expression of this `switch` expression. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Gets a result expression of this `switch` expression. */
|
||||
Expr getAResult() {
|
||||
result = getACase().getRuleExpression()
|
||||
or
|
||||
exists(BreakStmt break |
|
||||
break.(JumpStmt).getTarget() = this and result = break.getValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A parenthesised expression. */
|
||||
class ParExpr extends Expr, @parexpr {
|
||||
/** Gets the expression inside the parentheses. */
|
||||
|
||||
@@ -24,6 +24,14 @@ class Stmt extends StmtParent, ExprParent, @stmt {
|
||||
/** Gets the parent of this statement. */
|
||||
StmtParent getParent() { stmts(this, _, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the statement containing this statement, if any.
|
||||
*/
|
||||
Stmt getEnclosingStmt() {
|
||||
result = this.getParent() or
|
||||
result = this.getParent().(SwitchExpr).getEnclosingStmt()
|
||||
}
|
||||
|
||||
/** Holds if this statement is the child of the specified parent at the specified (zero-based) position. */
|
||||
predicate isNthChildOf(StmtParent parent, int index) {
|
||||
this.getParent() = parent and this.getIndex() = index
|
||||
@@ -400,23 +408,63 @@ class SwitchStmt extends Stmt, @switchstmt {
|
||||
}
|
||||
|
||||
/**
|
||||
* A case of a `switch` statement.
|
||||
* A case of a `switch` statement or expression.
|
||||
*
|
||||
* This includes both normal `case`s and the `default` case.
|
||||
*/
|
||||
class SwitchCase extends Stmt, @case {
|
||||
/** Gets the switch statement to which this case belongs, if any. */
|
||||
SwitchStmt getSwitch() { result.getACase() = this }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Gets the switch expression to which this case belongs, if any.
|
||||
*/
|
||||
SwitchExpr getSwitchExpr() { result.getACase() = this }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Holds if this `case` is a switch labeled rule of the form `... -> ...`.
|
||||
*/
|
||||
predicate isRule() {
|
||||
exists(Expr e | e.getParent() = this | e.getIndex() = -1)
|
||||
or
|
||||
exists(Stmt s | s.getParent() = this | s.getIndex() = -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Gets the expression on the right-hand side of the arrow, if any.
|
||||
*/
|
||||
Expr getRuleExpression() { result.getParent() = this and result.getIndex() = -1 }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Gets the statement on the right-hand side of the arrow, if any.
|
||||
*/
|
||||
Stmt getRuleStatement() { result.getParent() = this and result.getIndex() = -1 }
|
||||
}
|
||||
|
||||
/** A constant `case` of a switch statement. */
|
||||
class ConstCase extends SwitchCase {
|
||||
ConstCase() { exists(Expr e | e.getParent() = this) }
|
||||
ConstCase() { exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
|
||||
/** Gets the expression of this `case`. */
|
||||
Expr getValue() { result.getParent() = this }
|
||||
/** Gets the `case` constant at index 0. */
|
||||
Expr getValue() { result.getParent() = this and result.getIndex() = 0 }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Gets the `case` constant at the specified index.
|
||||
*/
|
||||
Expr getValue(int i) { result.getParent() = this and result.getIndex() = i and i >= 0 }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "case ...:" }
|
||||
override string pp() { result = "case ..." }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
override string getHalsteadID() { result = "ConstCase" }
|
||||
@@ -424,7 +472,7 @@ class ConstCase extends SwitchCase {
|
||||
|
||||
/** A `default` case of a `switch` statement */
|
||||
class DefaultCase extends SwitchCase {
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this) }
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() { result = "default" }
|
||||
@@ -485,12 +533,12 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
}
|
||||
|
||||
private Stmt findEnclosing() {
|
||||
result = getParent()
|
||||
result = getEnclosingStmt()
|
||||
or
|
||||
exists(Stmt mid |
|
||||
mid = findEnclosing() and
|
||||
not exists(this.catchClauseForThis(mid.(TryStmt))) and
|
||||
result = mid.getParent()
|
||||
result = mid.getEnclosingStmt()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -498,7 +546,7 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
result = try.getACatchClause() and
|
||||
result.getEnclosingCallable() = this.getEnclosingCallable() and
|
||||
getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and
|
||||
not this.getParent+() = result
|
||||
not this.getEnclosingStmt+() = result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,14 +562,14 @@ class JumpStmt extends Stmt {
|
||||
* `continue` statement refers to, if any.
|
||||
*/
|
||||
LabeledStmt getTargetLabel() {
|
||||
this.getParent+() = result and
|
||||
this.getEnclosingStmt+() = result and
|
||||
namestrings(result.getLabel(), _, this)
|
||||
}
|
||||
|
||||
private Stmt getLabelTarget() { result = getTargetLabel().getStmt() }
|
||||
|
||||
private Stmt getAPotentialTarget() {
|
||||
this.getParent+() = result and
|
||||
this.getEnclosingStmt+() = result and
|
||||
(
|
||||
result instanceof LoopStmt
|
||||
or
|
||||
@@ -529,15 +577,22 @@ class JumpStmt extends Stmt {
|
||||
)
|
||||
}
|
||||
|
||||
private Stmt getEnclosingTarget() {
|
||||
private SwitchExpr getSwitchExprTarget() {
|
||||
this.(BreakStmt).hasValue() and result = this.getParent+()
|
||||
}
|
||||
|
||||
private StmtParent getEnclosingTarget() {
|
||||
result = getSwitchExprTarget()
|
||||
or
|
||||
not exists(getSwitchExprTarget()) and
|
||||
result = getAPotentialTarget() and
|
||||
not exists(Stmt other | other = getAPotentialTarget() | other.getParent+() = result)
|
||||
not exists(Stmt other | other = getAPotentialTarget() | other.getEnclosingStmt+() = result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the statement that this `break` or `continue` jumps to.
|
||||
* Gets the statement or `switch` expression that this `break` or `continue` jumps to.
|
||||
*/
|
||||
Stmt getTarget() {
|
||||
StmtParent getTarget() {
|
||||
result = getLabelTarget()
|
||||
or
|
||||
not exists(getLabelTarget()) and result = getEnclosingTarget()
|
||||
@@ -552,9 +607,25 @@ class BreakStmt extends Stmt, @breakstmt {
|
||||
/** Holds if this `break` statement has an explicit label. */
|
||||
predicate hasLabel() { exists(string s | s = this.getLabel()) }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Gets the value of this `break` statement, if any.
|
||||
*/
|
||||
Expr getValue() { result.getParent() = this }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 12. Subject to removal in a future release.
|
||||
*
|
||||
* Holds if this `break` statement has a value.
|
||||
*/
|
||||
predicate hasValue() { exists(Expr e | e.getParent() = this) }
|
||||
|
||||
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
|
||||
override string pp() {
|
||||
if this.hasLabel() then result = "break " + this.getLabel() else result = "break"
|
||||
if this.hasLabel()
|
||||
then result = "break " + this.getLabel()
|
||||
else if this.hasValue() then result = "break ..." else result = "break"
|
||||
}
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
|
||||
@@ -184,7 +184,7 @@ private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
not v instanceof SsaPhiNode and
|
||||
not clearlyNotNull(v) and
|
||||
// Comparisons in finally blocks are excluded since missing exception edges in the CFG could otherwise yield FPs.
|
||||
not exists(TryStmt try | try.getFinally() = e.getEnclosingStmt().getParent*()) and
|
||||
not exists(TryStmt try | try.getFinally() = e.getEnclosingStmt().getEnclosingStmt*()) and
|
||||
(
|
||||
exists(ConditionalExpr c | c.getCondition().getAChildExpr*() = e) or
|
||||
not exists(MethodAccess ma | ma.getAnArgument().getAChildExpr*() = e)
|
||||
@@ -291,8 +291,8 @@ private predicate leavingFinally(BasicBlock bb1, BasicBlock bb2, boolean normale
|
||||
exists(TryStmt try, Block finally |
|
||||
try.getFinally() = finally and
|
||||
bb1.getABBSuccessor() = bb2 and
|
||||
bb1.getEnclosingStmt().getParent*() = finally and
|
||||
not bb2.getEnclosingStmt().getParent*() = finally and
|
||||
bb1.getEnclosingStmt().getEnclosingStmt*() = finally and
|
||||
not bb2.getEnclosingStmt().getEnclosingStmt*() = finally and
|
||||
if bb1.getLastNode().getANormalSuccessor() = bb2.getFirstNode()
|
||||
then normaledge = true
|
||||
else normaledge = false
|
||||
|
||||
@@ -166,7 +166,7 @@ private module TrackedVariablesImpl {
|
||||
/** Holds if `f` is accessed inside a loop. */
|
||||
private predicate loopAccessed(SsaSourceField f) {
|
||||
exists(LoopStmt l, FieldRead fr | fr = f.getAnAccess() |
|
||||
l.getBody() = fr.getEnclosingStmt().getParent*() or
|
||||
l.getBody() = fr.getEnclosingStmt().getEnclosingStmt*() or
|
||||
l.getCondition() = fr.getParent*() or
|
||||
l.(ForStmt).getAnUpdate() = fr.getParent*()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user