From df8ce5df72a3da3e1ee058f2cfcaa45c76bd6285 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 12 Nov 2024 17:01:51 +0000 Subject: [PATCH 01/16] Java: IPA the CFG (WIP) --- .../lib/semmle/code/java/ControlFlowGraph.qll | 288 +++++++++++------- 1 file changed, 178 insertions(+), 110 deletions(-) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index f8e94dc76844..b0731bbddda9 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -4,7 +4,7 @@ * The only API exported by this library are the toplevel classes `ControlFlowNode` * and its subclass `ConditionNode`, which wrap the successor relation and the * concept of true- and false-successors of conditions. A cfg node may either be a - * statement, an expression, or the enclosing callable, indicating that + * statement, an expression, or an exit node for a callable, indicating that * execution of the callable terminates. */ @@ -84,45 +84,107 @@ private import Completion private import controlflow.internal.Preconditions private import controlflow.internal.SwitchCases -/** A node in the expression-level control-flow graph. */ -class ControlFlowNode extends Top, @exprparent { - /** Gets the statement containing this node, if any. */ - Stmt getEnclosingStmt() { - result = this or - result = this.(Expr).getEnclosingStmt() +module ControlFlow { + private predicate hasControlFlow(Expr e) { + not e.getEnclosingStmt() instanceof ConstCase and + not e.getParent*() instanceof Annotation and + not e instanceof TypeAccess and + not e instanceof ArrayTypeAccess and + not e instanceof UnionTypeAccess and + not e instanceof IntersectionTypeAccess and + not e instanceof WildcardTypeAccess and + not exists(AssignExpr ae | ae.getDest() = e) } - /** Gets the immediately enclosing callable whose body contains this node. */ - Callable getEnclosingCallable() { - result = this or - result = this.(Stmt).getEnclosingCallable() or - result = this.(Expr).getEnclosingCallable() - } + private newtype TNode = + TExprNode(Expr e) { hasControlFlow(e) } or + TStmtNode(Stmt s) or + TExitNode(Callable c) { exists(c.getBody()) } + + /** A node in the expression-level control-flow graph. */ + class Node extends TNode { + /** Gets the statement containing this node, if any. */ + Stmt getEnclosingStmt() { + result = this.asStmt() or + result = this.asExpr().getEnclosingStmt() + } + + /** Gets the immediately enclosing callable whose body contains this node. */ + Callable getEnclosingCallable() { + this = TExitNode(result) or + result = this.asStmt().getEnclosingCallable() or + result = this.asExpr().getEnclosingCallable() + } + + /** Gets the statement this `Node` corresponds to, if any. */ + Stmt asStmt() { this = TStmtNode(result) } + + /** Gets the expression this `Node` corresponds to, if any. */ + Expr asExpr() { this = TExprNode(result) } + + /** Gets the call this `Node` corresponds to, if any. */ + Call asCall() { + result = this.asExpr() or + result = this.asStmt() + } + + /** Gets an immediate successor of this node. */ + Node getASuccessor() { result = succ(this) } + + /** Gets an immediate predecessor of this node. */ + Node getAPredecessor() { this = succ(result) } + + /** Gets an exception successor of this node. */ + Node getAnExceptionSuccessor() { result = succ(this, ThrowCompletion(_)) } - /** Gets an immediate successor of this node. */ - ControlFlowNode getASuccessor() { result = succ(this) } + /** Gets a successor of this node that is neither an exception successor nor a jump (break, continue, return). */ + Node getANormalSuccessor() { + result = succ(this, BooleanCompletion(_, _)) or + result = succ(this, NormalCompletion()) + } - /** Gets an immediate predecessor of this node. */ - ControlFlowNode getAPredecessor() { this = succ(result) } + /** Gets the basic block that contains this node. */ + BasicBlock getBasicBlock() { result.getANode() = this } - /** Gets an exception successor of this node. */ - ControlFlowNode getAnExceptionSuccessor() { result = succ(this, ThrowCompletion(_)) } + /** Gets a textual representation of this element. */ + string toString() { + result = this.asExpr().toString() + or + result = this.asStmt().toString() + or + result = "Exit" and this instanceof ExitNode + } - /** Gets a successor of this node that is neither an exception successor nor a jump (break, continue, return). */ - ControlFlowNode getANormalSuccessor() { - result = succ(this, BooleanCompletion(_, _)) or - result = succ(this, NormalCompletion()) + Location getLocation() { + result = this.asExpr().getLocation() or + result = this.asStmt().getLocation() or + result = this.(ExitNode).getEnclosingCallable().getLocation() + } } - /** Gets the basic block that contains this node. */ - BasicBlock getBasicBlock() { result.getANode() = this } + class ExitNode extends Node, TExitNode { } } +class ControlFlowNode = ControlFlow::Node; + /** Gets the intra-procedural successor of `n`. */ private ControlFlowNode succ(ControlFlowNode n) { result = succ(n, _) } cached private module ControlFlowGraphImpl { + private import ControlFlow + + private class AstNode extends ExprParent { + AstNode() { this instanceof Expr or this instanceof Stmt } + + Stmt getEnclosingStmt() { + result = this or + result = this.(Expr).getEnclosingStmt() + } + + Node getCFGNode() { result.asExpr() = this or result.asStmt() = this } + } + /** * Gets a label that applies to this statement. */ @@ -167,7 +229,7 @@ private module ControlFlowGraphImpl { * `ClassCastException` is expected, or because it is a Kotlin not-null check * and a `NullPointerException` is expected. */ - private predicate mayThrow(ControlFlowNode n, ThrowableType t) { + private predicate mayThrow(AstNode n, ThrowableType t) { t = n.(ThrowStmt).getThrownExceptionType() or exists(Call c | c = n | @@ -200,7 +262,7 @@ private module ControlFlowGraphImpl { * Bind `t` to an unchecked exception that may transfer control to a finally * block inside which `n` is nested. */ - private predicate uncheckedExceptionFromFinally(ControlFlowNode n, ThrowableType t) { + private predicate uncheckedExceptionFromFinally(AstNode n, ThrowableType t) { exists(TryStmt try | n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or n.(Expr).getParent*() = try.getAResource() @@ -214,7 +276,7 @@ private module ControlFlowGraphImpl { * Bind `t` to all unchecked exceptions that may be caught by some * `try-catch` inside which `n` is nested. */ - private predicate uncheckedExceptionFromCatch(ControlFlowNode n, ThrowableType t) { + private predicate uncheckedExceptionFromCatch(AstNode n, ThrowableType t) { exists(TryStmt try, UncheckedThrowableSuperType caught | n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or n.(Expr).getParent*() = try.getAResource() @@ -229,7 +291,7 @@ private module ControlFlowGraphImpl { * body or the resources (if any) of `try`. */ private ThrowableType thrownInBody(TryStmt try) { - exists(ControlFlowNode n | mayThrow(n, result) | + exists(AstNode n | mayThrow(n, result) | n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or n.(Expr).getParent*() = try.getAResource() ) @@ -287,7 +349,7 @@ private module ControlFlowGraphImpl { * That is, contexts where the control-flow edges depend on `value` given that `b` ends * with a `booleanCompletion(value, _)`. */ - private predicate inBooleanContext(ControlFlowNode b) { + private predicate inBooleanContext(AstNode b) { exists(LogicExpr logexpr | logexpr.(BinaryExpr).getLeftOperand() = b or @@ -493,9 +555,7 @@ private module ControlFlowGraphImpl { * immediately before either falling through to execute successor statements or execute a rule body * if present. `completion` is the completion kind of the last operation. */ - private predicate lastPatternCaseMatchingOp( - PatternCase pc, ControlFlowNode last, Completion completion - ) { + private predicate lastPatternCaseMatchingOp(PatternCase pc, Node last, Completion completion) { last(pc.getAPattern(), last, completion) and completion = NormalCompletion() and not exists(pc.getGuard()) @@ -514,7 +574,7 @@ private module ControlFlowGraphImpl { * and `ThrowStmt`. CFG nodes without child nodes in the CFG that may complete * normally are also included. */ - private class PostOrderNode extends ControlFlowNode { + private class PostOrderNode extends AstNode { PostOrderNode() { // For VarAccess and ArrayAccess only read accesses (r-values) are included, // as write accesses aren't included in the CFG. @@ -576,7 +636,7 @@ private module ControlFlowGraphImpl { } /** Gets child nodes in their order of execution. Indexing starts at either -1 or 0. */ - ControlFlowNode getChildNode(int index) { + AstNode getChildNode(int index) { exists(ArrayAccess e | e = this | index = 0 and result = e.getArray() or @@ -649,7 +709,7 @@ private module ControlFlowGraphImpl { } /** Gets the first child node, if any. */ - ControlFlowNode firstChild() { + AstNode firstChild() { result = this.getChildNode(-1) or result = this.getChildNode(0) and not exists(this.getChildNode(-1)) @@ -687,18 +747,18 @@ private module ControlFlowGraphImpl { /** * Determine the part of the AST node `n` that will be executed first. */ - private ControlFlowNode first(ControlFlowNode n) { - result = n and n instanceof LogicExpr + private Node first(AstNode n) { + result.asExpr() = n and n instanceof LogicExpr or - result = n and n instanceof ConditionalExpr + result.asExpr() = n and n instanceof ConditionalExpr or - result = n and n instanceof WhenExpr + result.asExpr() = n and n instanceof WhenExpr or - result = n and n instanceof WhenBranch + result.asExpr() = n and n instanceof WhenBranch or - result = n and n instanceof StmtExpr + result.asExpr() = n and n instanceof StmtExpr or - result = n and n.(PostOrderNode).isLeafNode() + result = n.getCFGNode() and n.(PostOrderNode).isLeafNode() or result = first(n.(PostOrderNode).firstChild()) or @@ -706,12 +766,11 @@ private module ControlFlowGraphImpl { or result = first(n.(SynchronizedStmt).getExpr()) or - result = n and - n instanceof Stmt and + result.asStmt() = n and not n instanceof PostOrderNode and not n instanceof SynchronizedStmt or - result = n and n instanceof SwitchExpr + result.asExpr() = n and n instanceof SwitchExpr } /** @@ -722,9 +781,7 @@ private module ControlFlowGraphImpl { * node in the `try` block that may not complete normally, or a node in * the `try` block that has no control flow successors inside the block. */ - private predicate catchOrFinallyCompletion( - TryStmt try, ControlFlowNode last, Completion completion - ) { + private predicate catchOrFinallyCompletion(TryStmt try, Node last, Completion completion) { last(try.getBlock(), last, completion) or last(try.getAResource(), last, completion) and completion = ThrowCompletion(_) @@ -737,7 +794,7 @@ private module ControlFlowGraphImpl { * In other words, if `last` throws an exception it is possibly not caught by any * of the catch clauses. */ - private predicate uncaught(TryStmt try, ControlFlowNode last, Completion completion) { + private predicate uncaught(TryStmt try, Node last, Completion completion) { catchOrFinallyCompletion(try, last, completion) and ( exists(ThrowableType thrown | @@ -767,12 +824,12 @@ private module ControlFlowGraphImpl { * This is similar to `uncaught`, but also includes final statements of `catch` * clauses. */ - private predicate finallyPred(TryStmt try, ControlFlowNode last, Completion completion) { + private predicate finallyPred(TryStmt try, Node last, Completion completion) { uncaught(try, last, completion) or last(try.getACatchClause(), last, completion) } - private predicate lastInFinally(TryStmt try, ControlFlowNode last) { + private predicate lastInFinally(TryStmt try, Node last) { last(try.getFinally(), last, NormalCompletion()) } @@ -796,7 +853,7 @@ private module ControlFlowGraphImpl { * A `booleanCompletion` implies that `n` is an `Expr`. Any abnormal * completion besides `throwCompletion` implies that `n` is a `Stmt`. */ - private predicate last(ControlFlowNode n, ControlFlowNode last, Completion completion) { + private predicate last(AstNode n, Node last, Completion completion) { // Exceptions are propagated from any sub-expression. // As are any break, yield, continue, or return completions. exists(Expr e | e.getParent() = n | @@ -853,15 +910,18 @@ private module ControlFlowGraphImpl { ) or exists(InstanceOfExpr ioe | ioe.isPattern() and ioe = n | - last = n and completion = basicBooleanCompletion(false) + last.asExpr() = n and completion = basicBooleanCompletion(false) or last(ioe.getPattern(), last, NormalCompletion()) and completion = basicBooleanCompletion(true) ) or // The last node of a node executed in post-order is the node itself. - n.(PostOrderNode).mayCompleteNormally() and last = n and completion = NormalCompletion() + // n.(PostOrderNode).mayCompleteNormally() and last = n and completion = NormalCompletion() + exists(PostOrderNode p | p = n | + p.mayCompleteNormally() and last = p.getCFGNode() and completion = NormalCompletion() + ) or - last = n and completion = basicBooleanCompletion(n.(BooleanLiteral).getBooleanValue()) + last.asExpr() = n and completion = basicBooleanCompletion(n.(BooleanLiteral).getBooleanValue()) or // The last statement in a block is any statement that does not complete normally, // or the last statement. @@ -997,7 +1057,7 @@ private module ControlFlowGraphImpl { // * On success of its guard test, if it is not a rule (boolean true) // (the latter two cases are accounted for by lastPatternCaseMatchingOp) exists(PatternCase pc | n = pc | - last = pc and completion = basicBooleanCompletion(false) + last.asStmt() = pc and completion = basicBooleanCompletion(false) or last(pc.getGuard(), last, completion) and completion = BooleanCompletion(false, _) @@ -1010,13 +1070,15 @@ private module ControlFlowGraphImpl { last(n.(SynchronizedStmt).getBlock(), last, completion) or // `return` statements give rise to a `Return` completion - last = n.(ReturnStmt) and completion = ReturnCompletion() + last.asStmt() = n.(ReturnStmt) and completion = ReturnCompletion() or // `throw` statements or throwing calls give rise to ` Throw` completion - exists(ThrowableType tt | mayThrow(n, tt) | last = n and completion = ThrowCompletion(tt)) + exists(ThrowableType tt | mayThrow(n, tt) | + last = n.getCFGNode() and completion = ThrowCompletion(tt) + ) or // `break` statements give rise to a `Break` completion - exists(BreakStmt break | break = n and last = n | + exists(BreakStmt break | break = n and last.asStmt() = n | completion = labelledBreakCompletion(MkLabel(break.getLabel())) or not exists(break.getLabel()) and completion = anonymousBreakCompletion() @@ -1031,7 +1093,7 @@ private module ControlFlowGraphImpl { ) or // `continue` statements give rise to a `Continue` completion - exists(ContinueStmt cont | cont = n and last = n | + exists(ContinueStmt cont | cont = n and last.asStmt() = n | completion = labelledContinueCompletion(MkLabel(cont.getLabel())) or not exists(cont.getLabel()) and completion = anonymousContinueCompletion() @@ -1067,7 +1129,7 @@ private module ControlFlowGraphImpl { // the last node of the condition of the last branch in the absence of an else-branch. exists(WhenExpr whenexpr | whenexpr = n | // If we have no branches then we are the last node - last = n and + last.asExpr() = n and completion = NormalCompletion() and not exists(whenexpr.getBranch(_)) or @@ -1117,17 +1179,19 @@ private module ControlFlowGraphImpl { * execution finishes with the given completion. */ cached - ControlFlowNode succ(ControlFlowNode n, Completion completion) { - // Callables serve as their own exit nodes. - exists(Callable c | last(c.getBody(), n, completion) | result = c) + Node succ(Node n, Completion completion) { + // The successor of a callable is its exit node. + exists(Callable c | last(c.getBody(), n, completion) | + result.(ExitNode).getEnclosingCallable() = c + ) or // Logic expressions and conditional expressions execute in AST pre-order. completion = NormalCompletion() and ( - result = first(n.(AndLogicalExpr).getLeftOperand()) or - result = first(n.(OrLogicalExpr).getLeftOperand()) or - result = first(n.(LogNotExpr).getExpr()) or - result = first(n.(ConditionalExpr).getCondition()) + result = first(n.asExpr().(AndLogicalExpr).getLeftOperand()) or + result = first(n.asExpr().(OrLogicalExpr).getLeftOperand()) or + result = first(n.asExpr().(LogNotExpr).getExpr()) or + result = first(n.asExpr().(ConditionalExpr).getCondition()) ) or // If a logic expression doesn't short-circuit then control flows from its left operand to its right. @@ -1151,9 +1215,11 @@ private module ControlFlowGraphImpl { ) or exists(InstanceOfExpr ioe | ioe.isPattern() | - last(ioe.getExpr(), n, completion) and completion = NormalCompletion() and result = ioe + last(ioe.getExpr(), n, completion) and + completion = NormalCompletion() and + result.asExpr() = ioe or - n = ioe and + n.asExpr() = ioe and result = first(ioe.getPattern()) and completion = basicBooleanCompletion(true) ) @@ -1164,11 +1230,11 @@ private module ControlFlowGraphImpl { | result = first(p.getChildNode(i + 1)) or - not exists(p.getChildNode(i + 1)) and result = p + not exists(p.getChildNode(i + 1)) and result = p.getCFGNode() ) or // Statements within a block execute sequentially. - result = first(n.(BlockStmt).getStmt(0)) and completion = NormalCompletion() + result = first(n.asStmt().(BlockStmt).getStmt(0)) and completion = NormalCompletion() or exists(BlockStmt blk, int i | last(blk.getStmt(i), n, completion) and @@ -1178,7 +1244,7 @@ private module ControlFlowGraphImpl { or // Control flows to the corresponding branch depending on the boolean completion of the condition. exists(IfStmt s | - n = s and result = first(s.getCondition()) and completion = NormalCompletion() + n.asStmt() = s and result = first(s.getCondition()) and completion = NormalCompletion() or last(s.getCondition(), n, completion) and completion = BooleanCompletion(true, _) and @@ -1190,7 +1256,7 @@ private module ControlFlowGraphImpl { ) or // For statements: - exists(ForStmt for, ControlFlowNode condentry | + exists(ForStmt for, Node condentry | // Any part of the control flow that aims for the condition needs to hit either the condition... condentry = first(for.getCondition()) or @@ -1198,10 +1264,10 @@ private module ControlFlowGraphImpl { not exists(for.getCondition()) and condentry = first(for.getStmt()) | // From the entry point, which is the for statement itself, control goes to either the first init expression... - n = for and result = first(for.getInit(0)) and completion = NormalCompletion() + n.asStmt() = for and result = first(for.getInit(0)) and completion = NormalCompletion() or // ...or the condition if the for doesn't include init expressions. - n = for and + n.asStmt() = for and not exists(for.getAnInit()) and result = condentry and completion = NormalCompletion() @@ -1238,27 +1304,29 @@ private module ControlFlowGraphImpl { // Enhanced for statements: exists(EnhancedForStmt for | // First the expression gets evaluated... - n = for and result = first(for.getExpr()) and completion = NormalCompletion() + n.asStmt() = for and result = first(for.getExpr()) and completion = NormalCompletion() or // ...then the variable gets assigned... last(for.getExpr(), n, completion) and completion = NormalCompletion() and - result = for.getVariable() + result.asExpr() = for.getVariable() or // ...and then control goes to the body of the loop. - n = for.getVariable() and result = first(for.getStmt()) and completion = NormalCompletion() + n.asExpr() = for.getVariable() and + result = first(for.getStmt()) and + completion = NormalCompletion() or // Finally, the back edge of the loop goes to reassign the variable. last(for.getStmt(), n, completion) and continues(completion, for) and - result = for.getVariable() + result.asExpr() = for.getVariable() ) or // While loops start at the condition... - result = first(n.(WhileStmt).getCondition()) and completion = NormalCompletion() + result = first(n.asStmt().(WhileStmt).getCondition()) and completion = NormalCompletion() or // ...and do-while loops start at the body. - result = first(n.(DoStmt).getStmt()) and completion = NormalCompletion() + result = first(n.asStmt().(DoStmt).getStmt()) and completion = NormalCompletion() or exists(LoopStmt loop | loop instanceof WhileStmt or loop instanceof DoStmt | // Control goes from the condition via a true-completion to the body... @@ -1282,7 +1350,7 @@ private module ControlFlowGraphImpl { ) or // After the last resource declaration, control transfers to the body. - exists(TryStmt try | n = try and completion = NormalCompletion() | + exists(TryStmt try | n.asStmt() = try and completion = NormalCompletion() | result = first(try.getResource(0)) or not exists(try.getAResource()) and result = first(try.getBlock()) @@ -1310,7 +1378,7 @@ private module ControlFlowGraphImpl { or // Catch clauses first assign their variable and then execute their block exists(CatchClause cc | completion = NormalCompletion() | - n = cc and result = first(cc.getVariable()) + n.asStmt() = cc and result = first(cc.getVariable()) or last(cc.getVariable(), n, completion) and result = first(cc.getBlock()) ) @@ -1321,7 +1389,9 @@ private module ControlFlowGraphImpl { switchExpr = switch.(SwitchStmt).getExpr() or switchExpr = switch.(SwitchExpr).getExpr() | // From the entry point control is transferred first to the expression... - n = switch and result = first(switchExpr) and completion = NormalCompletion() + n = switch.(AstNode).getCFGNode() and + result = first(switchExpr) and + completion = NormalCompletion() or // ...and then to any case up to and including the first pattern case, if any. last(switchExpr, n, completion) and @@ -1345,7 +1415,7 @@ private module ControlFlowGraphImpl { or // A pattern case that completes boolean false (type test or guard failure) continues to consider other cases: exists(PatternCase case | completion = BooleanCompletion(false, _) | - last(case, n, completion) and result = getASuccessorSwitchCase(case, switch) + last(case, n, completion) and result.asStmt() = getASuccessorSwitchCase(case, switch) ) ) or @@ -1358,7 +1428,7 @@ private module ControlFlowGraphImpl { // * Variable declarations -normal-> rule execution (when there is no guard) // * Guard success -true-> rule execution exists(PatternCase pc | - n = pc and + n.asStmt() = pc and completion = basicBooleanCompletion(true) and result = first(pc.getAPattern()) or @@ -1375,7 +1445,7 @@ private module ControlFlowGraphImpl { ) or // Non-pattern cases have an internal edge leading to their rule body if any when the case matches. - exists(SwitchCase case | n = case | + exists(SwitchCase case | n.asStmt() = case | not case instanceof PatternCase and completion = NormalCompletion() and ( @@ -1387,32 +1457,32 @@ private module ControlFlowGraphImpl { or // Yield exists(YieldStmt yield | completion = NormalCompletion() | - n = yield and result = first(yield.getValue()) + n.asStmt() = yield and result = first(yield.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 + last(synch.getExpr(), n, completion) and result.asStmt() = synch or - n = synch and result = first(synch.getBlock()) + n.asStmt() = synch and result = first(synch.getBlock()) ) or - result = first(n.(ExprStmt).getExpr()) and completion = NormalCompletion() + result = first(n.asStmt().(ExprStmt).getExpr()) and completion = NormalCompletion() or - result = first(n.(StmtExpr).getStmt()) and completion = NormalCompletion() + result = first(n.asExpr().(StmtExpr).getStmt()) and completion = NormalCompletion() or - result = first(n.(LabeledStmt).getStmt()) and completion = NormalCompletion() + result = first(n.asStmt().(LabeledStmt).getStmt()) and completion = NormalCompletion() or // Variable declarations in a variable declaration statement are executed sequentially. exists(LocalVariableDeclStmt s | completion = NormalCompletion() | - n = s and result = first(s.getVariable(1)) + n.asStmt() = s and result = first(s.getVariable(1)) or exists(int i | last(s.getVariable(i), n, completion) and result = first(s.getVariable(i + 1))) ) or // When expressions: exists(WhenExpr whenexpr | - n = whenexpr and + n.asExpr() = whenexpr and result = first(whenexpr.getBranch(0)) and completion = NormalCompletion() or @@ -1425,7 +1495,7 @@ private module ControlFlowGraphImpl { or // When branches: exists(WhenBranch whenbranch | - n = whenbranch and + n.asStmt() = whenbranch and completion = NormalCompletion() and result = first(whenbranch.getCondition()) or @@ -1463,7 +1533,7 @@ private module ControlFlowGraphImpl { * predicate `finallyPred`, since their completion is resumed after normal * completion of the `finally`. */ - private Completion resumption(ControlFlowNode n) { + private Completion resumption(Node n) { exists(TryStmt try | lastInFinally(try, n) and finallyPred(try, _, result)) or not lastInFinally(_, n) and result = NormalCompletion() @@ -1474,9 +1544,7 @@ private module ControlFlowGraphImpl { * * That is, the `booleanCompletion` is the label of the edge in the CFG. */ - private ControlFlowNode mainBranchSucc(ControlFlowNode n, boolean b) { - result = succ(n, BooleanCompletion(_, b)) - } + private Node mainBranchSucc(Node n, boolean b) { result = succ(n, BooleanCompletion(_, b)) } /** * A true- or false-successor that is not tagged with a `booleanCompletion`. @@ -1487,8 +1555,8 @@ private module ControlFlowGraphImpl { * In the latter case, when `n` occurs as the last node in a finally block, there might be * multiple different such successors. */ - private ControlFlowNode otherBranchSucc(ControlFlowNode n, boolean b) { - exists(ControlFlowNode main | main = mainBranchSucc(n, b.booleanNot()) | + private Node otherBranchSucc(Node n, boolean b) { + exists(Node main | main = mainBranchSucc(n, b.booleanNot()) | result = succ(n, resumption(n)) and not result = main and (b = true or b = false) @@ -1497,7 +1565,7 @@ private module ControlFlowGraphImpl { /** Gets a true- or false-successor of `n`. */ cached - ControlFlowNode branchSuccessor(ControlFlowNode n, boolean branch) { + Node branchSuccessor(Node n, boolean branch) { result = mainBranchSucc(n, branch) or result = otherBranchSucc(n, branch) } @@ -1506,18 +1574,18 @@ private module ControlFlowGraphImpl { private import ControlFlowGraphImpl /** A control-flow node that branches based on a condition. */ -class ConditionNode extends ControlFlowNode { +class ConditionNode extends ControlFlow::Node { ConditionNode() { exists(branchSuccessor(this, _)) } /** Gets a true- or false-successor of the `ConditionNode`. */ - ControlFlowNode getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) } + ControlFlow::Node getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) } /** Gets a true-successor of the `ConditionNode`. */ - ControlFlowNode getATrueSuccessor() { result = this.getABranchSuccessor(true) } + ControlFlow::Node getATrueSuccessor() { result = this.getABranchSuccessor(true) } /** Gets a false-successor of the `ConditionNode`. */ - ControlFlowNode getAFalseSuccessor() { result = this.getABranchSuccessor(false) } + ControlFlow::Node getAFalseSuccessor() { result = this.getABranchSuccessor(false) } /** Gets the condition of this `ConditionNode`. This is equal to the node itself. */ - ExprParent getCondition() { result = this } + ExprParent getCondition() { result = this.asExpr() or result = this.asStmt() } } From 37a1b0b2fbaf22faf433a4b75fc6c57dc7b1db81 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 13 Nov 2024 11:13:19 +0100 Subject: [PATCH 02/16] Java: some mechanical transformations Only one in Dominance required thinking. --- java/ql/lib/semmle/code/java/Expr.qll | 4 +-- java/ql/lib/semmle/code/java/Statement.qll | 4 +-- .../code/java/controlflow/Dominance.qll | 21 ++++++----- .../semmle/code/java/controlflow/Guards.qll | 6 ++-- .../semmle/code/java/controlflow/Paths.qll | 18 ++++++---- java/ql/lib/semmle/code/java/dataflow/SSA.qll | 35 +++++++++++-------- .../semmle/code/java/dataflow/TypeFlow.qll | 4 +-- 7 files changed, 50 insertions(+), 42 deletions(-) diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 1862319e30bb..24e5a6e24d8b 100644 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -61,10 +61,10 @@ class Expr extends ExprParent, @expr { Expr getAChildExpr() { exprs(result, _, _, this, _) } /** Gets the basic block in which this expression occurs, if any. */ - BasicBlock getBasicBlock() { result.getANode() = this } + BasicBlock getBasicBlock() { result.getANode().asExpr() = this } /** Gets the `ControlFlowNode` corresponding to this expression. */ - ControlFlowNode getControlFlowNode() { result = this } + ControlFlowNode getControlFlowNode() { result.asExpr() = this } /** This statement's Halstead ID (used to compute Halstead metrics). */ string getHalsteadID() { result = this.toString() } diff --git a/java/ql/lib/semmle/code/java/Statement.qll b/java/ql/lib/semmle/code/java/Statement.qll index f4eafd39e9ff..da9621f9ce3a 100644 --- a/java/ql/lib/semmle/code/java/Statement.qll +++ b/java/ql/lib/semmle/code/java/Statement.qll @@ -45,10 +45,10 @@ class Stmt extends StmtParent, ExprParent, @stmt { Stmt getAChild() { result.getParent() = this } /** Gets the basic block in which this statement occurs. */ - BasicBlock getBasicBlock() { result.getANode() = this } + BasicBlock getBasicBlock() { result.getANode().asStmt() = this } /** Gets the `ControlFlowNode` corresponding to this statement. */ - ControlFlowNode getControlFlowNode() { result = this } + ControlFlowNode getControlFlowNode() { result.asStmt() = this } /** Cast this statement to a class that provides access to metrics information. */ MetricStmt getMetrics() { result = this } diff --git a/java/ql/lib/semmle/code/java/controlflow/Dominance.qll b/java/ql/lib/semmle/code/java/controlflow/Dominance.qll index a1263ce3f0eb..953bfa12e640 100644 --- a/java/ql/lib/semmle/code/java/controlflow/Dominance.qll +++ b/java/ql/lib/semmle/code/java/controlflow/Dominance.qll @@ -9,13 +9,15 @@ import java */ /** Entry points for control-flow. */ -private predicate flowEntry(Stmt entry) { - exists(Callable c | entry = c.getBody()) - or - // This disjunct is technically superfluous, but safeguards against extractor problems. - entry instanceof BlockStmt and - not exists(entry.getEnclosingCallable()) and - not entry.getParent() instanceof Stmt +private predicate flowEntry(BasicBlock entry) { + exists(Stmt entrystmt | entrystmt = entry.getFirstNode().asStmt() | + exists(Callable c | entrystmt = c.getBody()) + or + // This disjunct is technically superfluous, but safeguards against extractor problems. + entrystmt instanceof BlockStmt and + not exists(entry.getEnclosingCallable()) and + not entrystmt.getParent() instanceof Stmt + ) } /** The successor relation for basic blocks. */ @@ -31,11 +33,8 @@ predicate hasDominanceInformation(BasicBlock bb) { exists(BasicBlock entry | flowEntry(entry) and bbSucc*(entry, bb)) } -/** Exit points for control-flow. */ -private predicate flowExit(Callable exit) { exists(ControlFlowNode s | s.getASuccessor() = exit) } - /** Exit points for basic-block control-flow. */ -private predicate bbSink(BasicBlock exit) { flowExit(exit.getLastNode()) } +private predicate bbSink(BasicBlock exit) { exit.getLastNode() instanceof ControlFlow::ExitNode } /** Reversed `bbSucc`. */ private predicate bbPred(BasicBlock post, BasicBlock pre) { post = pre.getABBSuccessor() } diff --git a/java/ql/lib/semmle/code/java/controlflow/Guards.qll b/java/ql/lib/semmle/code/java/controlflow/Guards.qll index 0d0ecd5b2ea4..ff564b3a4469 100644 --- a/java/ql/lib/semmle/code/java/controlflow/Guards.qll +++ b/java/ql/lib/semmle/code/java/controlflow/Guards.qll @@ -113,7 +113,7 @@ private PatternCase getClosestPrecedingPatternCase(SwitchCase case) { private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pred) { pred = sc.getControlFlowNode().getAPredecessor() and ( - pred.(Expr).getParent*() = sc.getSelectorExpr() + pred.asExpr().getParent*() = sc.getSelectorExpr() or // Ambiguous: in the case of `case String _ when x: case "SomeConstant":`, the guard `x` // passing edge will fall through into the constant case, and the guard failing edge @@ -122,7 +122,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre exists(PatternCase previousPatternCase | previousPatternCase = getClosestPrecedingPatternCase(sc) | - pred.(Expr).getParent*() = previousPatternCase.getGuard() and + pred.asExpr().getParent*() = previousPatternCase.getGuard() and // Check there is any statement in between the previous pattern case and this one, // or the case is a rule, so there is no chance of a fall-through. ( @@ -133,7 +133,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre or // Unambigious: on the test-passing edge there must be at least one intervening // declaration node, including anonymous `_` declarations. - pred = getClosestPrecedingPatternCase(sc) + pred.asStmt() = getClosestPrecedingPatternCase(sc) ) } diff --git a/java/ql/lib/semmle/code/java/controlflow/Paths.qll b/java/ql/lib/semmle/code/java/controlflow/Paths.qll index b4e9a68b280e..1fb36b9be369 100644 --- a/java/ql/lib/semmle/code/java/controlflow/Paths.qll +++ b/java/ql/lib/semmle/code/java/controlflow/Paths.qll @@ -32,7 +32,7 @@ abstract class ActionConfiguration extends string { private BasicBlock actionBlock(ActionConfiguration conf) { exists(ControlFlowNode node | result = node.getBasicBlock() | conf.isAction(node) or - callAlwaysPerformsAction(node, conf) + callAlwaysPerformsAction(node.asCall(), conf) ) } @@ -43,19 +43,23 @@ private predicate callAlwaysPerformsAction(Call call, ActionConfiguration conf) ) } +private class ExitBlock extends BasicBlock { + ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode } +} + /** Holds if an action dominates the exit of the callable. */ private predicate actionDominatesExit(Callable callable, ActionConfiguration conf) { - exists(BasicBlock exit | - exit.getLastNode() = callable and + exists(ExitBlock exit | + exit.getEnclosingCallable() = callable and actionBlock(conf).bbDominates(exit) ) } /** Gets a `BasicBlock` that contains an action that does not dominate the exit. */ private BasicBlock nonDominatingActionBlock(ActionConfiguration conf) { - exists(BasicBlock exit | + exists(ExitBlock exit | result = actionBlock(conf) and - exit.getLastNode() = result.getEnclosingCallable() and + exit.getEnclosingCallable() = result.getEnclosingCallable() and not result.bbDominates(exit) ) } @@ -80,8 +84,8 @@ private predicate postActionBlock(BasicBlock bb, ActionConfiguration conf) { private predicate callableAlwaysPerformsAction(Callable callable, ActionConfiguration conf) { actionDominatesExit(callable, conf) or - exists(BasicBlock exit | - exit.getLastNode() = callable and + exists(ExitBlock exit | + exit.getEnclosingCallable() = callable and postActionBlock(exit, conf) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/SSA.qll b/java/ql/lib/semmle/code/java/dataflow/SSA.qll index 0fc0da8e8715..ce58e7d58dc1 100644 --- a/java/ql/lib/semmle/code/java/dataflow/SSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/SSA.qll @@ -228,7 +228,7 @@ private module SsaImpl { /** Holds if `n` must update the locally tracked variable `v`. */ cached predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) { - exists(VariableUpdate a | a = n | getDestVar(a) = v) and + exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and b.getNode(i) = n and hasDominanceInformation(b) or @@ -237,8 +237,8 @@ private module SsaImpl { /** Gets the definition point of a nested class in the parent scope. */ private ControlFlowNode parentDef(NestedClass nc) { - nc.(AnonymousClass).getClassInstanceExpr() = result or - nc.(LocalClass).getLocalTypeDeclStmt() = result + nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or + nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result } /** @@ -276,7 +276,7 @@ private module SsaImpl { /** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */ private predicate variableUse(TrackedVar v, VarRead use, BasicBlock b, int i) { - v.getAnAccess() = use and b.getNode(i) = use + v.getAnAccess() = use and b.getNode(i) = use.getControlFlowNode() } /** Holds if the value of `v` is captured in `b` at index `i`. */ @@ -423,7 +423,7 @@ private module SsaImpl { * `f` has an update somewhere. */ private predicate updateCandidate(TrackedField f, Call call, BasicBlock b, int i) { - b.getNode(i) = call and + b.getNode(i).asCall() = call and call.getEnclosingCallable() = f.getEnclosingCallable() and relevantFieldUpdate(_, f.getField(), _) } @@ -550,7 +550,7 @@ private module SsaImpl { /** Holds if `n` might update the locally tracked variable `v`. */ cached predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) { - exists(Call c | c = n | updatesNamedField(c, v, _)) and + exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and b.getNode(i) = n and hasDominanceInformation(b) or @@ -574,12 +574,16 @@ private module SsaImpl { /** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */ cached predicate hasEntryDef(TrackedVar v, BasicBlock b) { - exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b | + exists(LocalScopeVariable l, Callable c | + v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b + | l instanceof Parameter or l.getCallable() != c ) or - v instanceof SsaSourceField and v.getEnclosingCallable().getBody() = b and liveAtEntry(v, b) + v instanceof SsaSourceField and + v.getEnclosingCallable().getBody().getControlFlowNode() = b and + liveAtEntry(v, b) } /** @@ -882,7 +886,7 @@ private newtype TSsaVariable = } or TSsaEntryDef(TrackedVar v, BasicBlock b) { hasEntryDef(v, b) } or TSsaUntracked(SsaSourceField nf, ControlFlowNode n) { - n = nf.getAnAccess().(FieldRead) and not trackField(nf) + n = nf.getAnAccess().(FieldRead).getControlFlowNode() and not trackField(nf) } /** @@ -940,7 +944,7 @@ class SsaVariable extends TSsaVariable { /** Gets an access of this SSA variable. */ VarRead getAUse() { ssaDefReachesUse(_, this, result) or - this = TSsaUntracked(_, result) + this = TSsaUntracked(_, result.getControlFlowNode()) } /** @@ -954,7 +958,7 @@ class SsaVariable extends TSsaVariable { */ VarRead getAFirstUse() { firstUse(this, result) or - this = TSsaUntracked(_, result) + this = TSsaUntracked(_, result.getControlFlowNode()) } /** Holds if this SSA variable is live at the end of `b`. */ @@ -990,7 +994,7 @@ class SsaUpdate extends SsaVariable { class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate { SsaExplicitUpdate() { exists(VariableUpdate upd | - upd = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable() + upd.getControlFlowNode() = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable() ) } @@ -998,7 +1002,8 @@ class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate { /** Gets the `VariableUpdate` defining the SSA variable. */ VariableUpdate getDefiningExpr() { - result = this.getCfgNode() and getDestVar(result) = this.getSourceVariable() + result.getControlFlowNode() = this.getCfgNode() and + getDestVar(result) = this.getSourceVariable() } } @@ -1038,7 +1043,7 @@ class SsaImplicitUpdate extends SsaUpdate { exists(SsaSourceField f, Callable setter | f = this.getSourceVariable() and relevantFieldUpdate(setter, f.getField(), result) and - updatesNamedField(this.getCfgNode(), f, setter) + updatesNamedField(this.getCfgNode().asCall(), f, setter) ) } @@ -1086,7 +1091,7 @@ class SsaImplicitInit extends SsaVariable, TSsaEntryDef { */ predicate isParameterDefinition(Parameter p) { this.getSourceVariable() = TLocalVar(p.getCallable(), p) and - p.getCallable().getBody() = this.getCfgNode() + p.getCallable().getBody().getControlFlowNode() = this.getCfgNode() } } diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll index c548c5db38be..a69548380f27 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll @@ -235,8 +235,8 @@ private module Input implements TypeFlowInput { downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and t1.getASourceSupertype+() = t2 and va = v.getAUse() and - dominates(cast, va) and - dominates(cast.(ControlFlowNode).getANormalSuccessor(), va) + dominates(cast.getControlFlowNode(), va.getControlFlowNode()) and + dominates(cast.getControlFlowNode().getANormalSuccessor(), va.getControlFlowNode()) ) } From 35f534fbce3e4849475cd33a9ad95e1cec212149 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 13 Nov 2024 12:37:43 +0100 Subject: [PATCH 03/16] Java: More simple rewrites --- .../code/java/dataflow/InstanceAccess.qll | 8 ++++--- .../semmle/code/java/dataflow/Nullness.qll | 24 +++++++++---------- .../code/java/dataflow/internal/BaseSSA.qll | 19 ++++++++------- .../dataflow/internal/DataFlowPrivate.qll | 2 +- .../java/dataflow/internal/DataFlowUtil.qll | 6 +++-- .../rangeanalysis/SignAnalysisSpecific.qll | 2 +- .../code/java/metrics/MetricCallable.qll | 6 ++--- .../semmle/code/java/security/Validation.qll | 2 +- 8 files changed, 38 insertions(+), 31 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/InstanceAccess.qll b/java/ql/lib/semmle/code/java/dataflow/InstanceAccess.qll index 18bdb879c3cb..0bae1b5e9c10 100644 --- a/java/ql/lib/semmle/code/java/dataflow/InstanceAccess.qll +++ b/java/ql/lib/semmle/code/java/dataflow/InstanceAccess.qll @@ -227,12 +227,14 @@ class InstanceAccessExt extends TInstanceAccessExt { /** Gets the control flow node associated with this instance access. */ ControlFlowNode getCfgNode() { exists(ExprParent e | e = this.getAssociatedExprOrStmt() | - e instanceof Call and result = e + result.asCall() = e or - e instanceof InstanceAccess and result = e + e.(InstanceAccess).getControlFlowNode() = result or exists(FieldAccess fa | fa = e | - if fa instanceof VarRead then fa = result else result.(AssignExpr).getDest() = fa + if fa instanceof VarRead + then fa.getControlFlowNode() = result + else result.asExpr().(AssignExpr).getDest() = fa ) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/Nullness.qll b/java/ql/lib/semmle/code/java/dataflow/Nullness.qll index fb2fc668cf3a..618716629b10 100644 --- a/java/ql/lib/semmle/code/java/dataflow/Nullness.qll +++ b/java/ql/lib/semmle/code/java/dataflow/Nullness.qll @@ -130,8 +130,8 @@ predicate dereference(Expr e) { * The `VarAccess` is included for nicer error reporting. */ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) { - dereference(result) and - result = sameValue(v, va) + dereference(result.asExpr()) and + result.asExpr() = sameValue(v, va) } /** @@ -141,16 +141,16 @@ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) { private ControlFlowNode ensureNotNull(SsaVariable v) { result = varDereference(v, _) or - result.(AssertStmt).getExpr() = nullGuard(v, true, false) + result.asStmt().(AssertStmt).getExpr() = nullGuard(v, true, false) or - exists(AssertTrueMethod m | result = m.getACheck(nullGuard(v, true, false))) + exists(AssertTrueMethod m | result.asCall() = m.getACheck(nullGuard(v, true, false))) or - exists(AssertFalseMethod m | result = m.getACheck(nullGuard(v, false, false))) + exists(AssertFalseMethod m | result.asCall() = m.getACheck(nullGuard(v, false, false))) or - exists(AssertNotNullMethod m | result = m.getACheck(v.getAUse())) + exists(AssertNotNullMethod m | result.asCall() = m.getACheck(v.getAUse())) or exists(AssertThatMethod m, MethodCall ma | - result = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result + result.asCall() = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result | ma.getAnArgument().(MethodCall).getMethod().getName() = "notNullValue" ) @@ -279,10 +279,10 @@ private predicate enhancedForEarlyExit(EnhancedForStmt for, ControlFlowNode n1, exists(Expr forExpr | n1.getANormalSuccessor() = n2 and for.getExpr() = forExpr and - forExpr.getAChildExpr*() = n1 and - not forExpr.getAChildExpr*() = n2 and - n1.getANormalSuccessor() = for.getVariable() and - not n2 = for.getVariable() + forExpr.getAChildExpr*() = n1.asExpr() and + not forExpr.getAChildExpr*() = n2.asExpr() and + n1.getANormalSuccessor().asExpr() = for.getVariable() and + not n2.asExpr() = for.getVariable() ) } @@ -343,7 +343,7 @@ private predicate nullVarStep( not impossibleEdge(mid, bb) and not exists(boolean branch | nullGuard(midssa, branch, false).hasBranchEdge(mid, bb, branch)) and not (leavingFinally(mid, bb, true) and midstoredcompletion = true) and - if bb.getFirstNode() = any(TryStmt try | | try.getFinally()) + if bb.getFirstNode().asStmt() = any(TryStmt try | | try.getFinally()) then if bb.getFirstNode() = mid.getLastNode().getANormalSuccessor() then storedcompletion = false diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll index f33a6d7195f0..b6845e02a40b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -71,15 +71,15 @@ private module SsaImpl { /** Holds if `n` updates the local variable `v`. */ cached predicate variableUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - exists(VariableUpdate a | a = n | getDestVar(a) = v) and + exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and b.getNode(i) = n and hasDominanceInformation(b) } /** Gets the definition point of a nested class in the parent scope. */ private ControlFlowNode parentDef(NestedClass nc) { - nc.(AnonymousClass).getClassInstanceExpr() = result or - nc.(LocalClass).getLocalTypeDeclStmt() = result + nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or + nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result } /** @@ -119,7 +119,7 @@ private module SsaImpl { /** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */ private predicate variableUse(BaseSsaSourceVariable v, VarRead use, BasicBlock b, int i) { - v.getAnAccess() = use and b.getNode(i) = use + v.getAnAccess() = use and b.getNode(i) = use.getControlFlowNode() } /** Holds if the value of `v` is captured in `b` at index `i`. */ @@ -162,7 +162,9 @@ private module SsaImpl { /** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */ cached predicate hasEntryDef(BaseSsaSourceVariable v, BasicBlock b) { - exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b | + exists(LocalScopeVariable l, Callable c | + v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b + | l instanceof Parameter or l.getCallable() != c ) @@ -533,7 +535,7 @@ class BaseSsaVariable extends TBaseSsaVariable { class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate { BaseSsaUpdate() { exists(VariableUpdate upd | - upd = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable() + upd.getControlFlowNode() = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable() ) } @@ -541,7 +543,8 @@ class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate { /** Gets the `VariableUpdate` defining the SSA variable. */ VariableUpdate getDefiningExpr() { - result = this.getCfgNode() and getDestVar(result) = this.getSourceVariable() + result.getControlFlowNode() = this.getCfgNode() and + getDestVar(result) = this.getSourceVariable() } } @@ -562,7 +565,7 @@ class BaseSsaImplicitInit extends BaseSsaVariable, TSsaEntryDef { */ predicate isParameterDefinition(Parameter p) { this.getSourceVariable() = TLocalVar(p.getCallable(), p) and - p.getCallable().getBody() = this.getCfgNode() + p.getCallable().getBody().getControlFlowNode() = this.getCfgNode() } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 589d75c3635d..704e5714784c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -112,7 +112,7 @@ private module CaptureInput implements VariableCapture::InputSig { Location getLocation() { result = super.getLocation() } - predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i) } + predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i).asExpr() } } class VariableWrite extends Expr instanceof VariableUpdate { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll index c1fc51bff049..024453066f15 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -17,9 +17,11 @@ import DataFlowNodes::Public /** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */ private predicate thisAccess(Node n, ControlFlowNode cfgnode) { - n.(InstanceParameterNode).getCallable().getBody() = cfgnode + n.(InstanceParameterNode).getCallable().getBody() = cfgnode.asStmt() or - exists(InstanceAccess ia | ia = n.asExpr() and ia = cfgnode and ia.isOwnInstanceAccess()) + exists(InstanceAccess ia | + ia = n.asExpr() and ia.getControlFlowNode() = cfgnode and ia.isOwnInstanceAccess() + ) or n.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getCfgNode() = cfgnode } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index bcc11e265189..ee2e3bb24123 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -340,7 +340,7 @@ private module Impl { Field getField(FieldAccess fa) { result = fa.getField() } - Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode() } + Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().asExpr() } Guard getComparisonGuard(ComparisonExpr ce) { result = ce } } diff --git a/java/ql/lib/semmle/code/java/metrics/MetricCallable.qll b/java/ql/lib/semmle/code/java/metrics/MetricCallable.qll index a888050185e2..d3dca781e54b 100644 --- a/java/ql/lib/semmle/code/java/metrics/MetricCallable.qll +++ b/java/ql/lib/semmle/code/java/metrics/MetricCallable.qll @@ -73,14 +73,14 @@ class MetricCallable extends Callable { // so there should be a branching point for each non-default switch // case (ignoring those that just fall through to the next case). private predicate branchingSwitchCase(ConstCase sc) { - not sc.(ControlFlowNode).getASuccessor() instanceof SwitchCase and + not sc.getControlFlowNode().getASuccessor().asStmt() instanceof SwitchCase and not defaultFallThrough(sc) } private predicate defaultFallThrough(ConstCase sc) { - exists(DefaultCase default | default.(ControlFlowNode).getASuccessor() = sc) + exists(DefaultCase default | default.getControlFlowNode().getASuccessor().asStmt() = sc) or - defaultFallThrough(sc.(ControlFlowNode).getAPredecessor()) + defaultFallThrough(sc.getControlFlowNode().getAPredecessor().asStmt()) } /** Holds if `stmt` is a branching statement used for the computation of cyclomatic complexity. */ diff --git a/java/ql/lib/semmle/code/java/security/Validation.qll b/java/ql/lib/semmle/code/java/security/Validation.qll index b8183b10751b..50f0a9aab1b6 100644 --- a/java/ql/lib/semmle/code/java/security/Validation.qll +++ b/java/ql/lib/semmle/code/java/security/Validation.qll @@ -50,7 +50,7 @@ private predicate validatedAccess(VarAccess va) { bb.getNode(i + 1) = node.getANormalSuccessor() | bb.bbStrictlyDominates(va.getBasicBlock()) or - bb.getNode(any(int j | j > i)) = va + bb.getNode(any(int j | j > i)).asExpr() = va ) ) ) From 3a7cf696617b86d9e1d1daf57b27399f374120b6 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 13 Nov 2024 12:55:11 +0100 Subject: [PATCH 04/16] Java: one more now we need to sort out range analysis --- java/ql/lib/semmle/code/java/frameworks/Assertions.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/lib/semmle/code/java/frameworks/Assertions.qll b/java/ql/lib/semmle/code/java/frameworks/Assertions.qll index 287346b85360..e7f86b9bfd81 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Assertions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Assertions.qll @@ -109,12 +109,12 @@ predicate assertFail(BasicBlock bb, ControlFlowNode n) { bb = n.getBasicBlock() and ( exists(AssertTrueMethod m | - n = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = false)) + n.asExpr() = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = false)) ) or exists(AssertFalseMethod m | - n = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = true)) + n.asExpr() = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = true)) ) or - exists(AssertFailMethod m | n = m.getACheck()) or - n.(AssertStmt).getExpr().(BooleanLiteral).getBooleanValue() = false + exists(AssertFailMethod m | n.asExpr() = m.getACheck()) or + n.asStmt().(AssertStmt).getExpr().(BooleanLiteral).getBooleanValue() = false ) } From a7d3fd9408db6b52c8c7d75143a2f8d44bd9b7e7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 13 Nov 2024 13:58:01 +0100 Subject: [PATCH 05/16] Java: Final rewrites to get things to compile The equivalence relation needed for range analysis is now on underlying `ExprParent`s, as `BasicBlock` is now an IPA type and ids are opaque. --- java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 8 +++++++- java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll | 6 ++++-- .../internal/rangeanalysis/ModulusAnalysisSpecific.qll | 2 +- .../internal/rangeanalysis/SsaReadPositionSpecific.qll | 6 ++++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index b0731bbddda9..01726ad5a1d7 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -160,6 +160,12 @@ module ControlFlow { result = this.asStmt().getLocation() or result = this.(ExitNode).getEnclosingCallable().getLocation() } + + ExprParent getAstNode() { + result = this.asExpr() or + result = this.asStmt() or + this = TExitNode(result) + } } class ExitNode extends Node, TExitNode { } @@ -754,7 +760,7 @@ private module ControlFlowGraphImpl { or result.asExpr() = n and n instanceof WhenExpr or - result.asExpr() = n and n instanceof WhenBranch + result.asStmt() = n and n instanceof WhenBranch or result.asExpr() = n and n instanceof StmtExpr or diff --git a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll index e0055d53f08d..c950963b104a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll @@ -211,9 +211,11 @@ module Sem implements Semantic { BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getABBSuccessor() } - private predicate id(BasicBlock x, BasicBlock y) { x = y } + private predicate id(ExprParent x, ExprParent y) { x = y } - private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y) + private predicate idOfAst(ExprParent x, int y) = equivalenceRelation(id/2)(x, y) + + private predicate idOf(BasicBlock x, int y) { idOfAst(x.getAstNode(), y) } int getBlockId1(BasicBlock bb) { idOf(bb, result) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll index 35384874b0d6..c88b9946faad 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll @@ -133,5 +133,5 @@ module Private { predicate ssaUpdateStep = RU::ssaUpdateStep/3; - Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode() } + Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode().asExpr() } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll index 410dc6b5cfeb..8712ad635f5c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll @@ -15,9 +15,11 @@ class BasicBlock = BB::BasicBlock; /** Gets a basic block in which SSA variable `v` is read. */ BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() } -private predicate id(BasicBlock x, BasicBlock y) { x = y } +private predicate id(BB::ExprParent x, BB::ExprParent y) { x = y } -private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y) +private predicate idOfAst(BB::ExprParent x, int y) = equivalenceRelation(id/2)(x, y) + +private predicate idOf(BasicBlock x, int y) { idOfAst(x.getAstNode(), y) } private int getId(BasicBlock bb) { idOf(bb, result) } From 06ef793e7b1cff06e90595f385bfb9e4c6d86c5f Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Nov 2024 13:50:40 +0000 Subject: [PATCH 06/16] Fix compilation errors in tests --- java/ql/consistency-queries/cfgDeadEnds.ql | 15 +++++++++------ .../library-tests/controlflow/basic/bbStmts.ql | 2 +- .../controlflow/basic/strictDominance.ql | 2 +- .../controlflow/basic/strictPostDominance.ql | 2 +- .../controlflow/dominance/dominanceBad.ql | 4 ++-- .../controlflow/dominance/dominanceWrong.ql | 2 +- .../controlflow/dominance/dominatedByStart.ql | 6 +++--- .../controlflow/dominance/dominatorExists.ql | 4 ++-- .../test/library-tests/controlflow/paths/paths.ql | 2 +- 9 files changed, 21 insertions(+), 18 deletions(-) diff --git a/java/ql/consistency-queries/cfgDeadEnds.ql b/java/ql/consistency-queries/cfgDeadEnds.ql index 73c30015a6fc..2d5d23d92c43 100644 --- a/java/ql/consistency-queries/cfgDeadEnds.ql +++ b/java/ql/consistency-queries/cfgDeadEnds.ql @@ -1,7 +1,7 @@ import java -import semmle.code.java.ControlFlowGraph -predicate shouldBeDeadEnd(ControlFlowNode n) { +// import semmle.code.java.ControlFlowGraph +predicate shouldBeDeadEnd(ExprParent n) { n instanceof BreakStmt and n.getFile().isKotlinSourceFile() // TODO or n instanceof Interface // TODO @@ -55,8 +55,11 @@ predicate shouldBeDeadEnd(ControlFlowNode n) { n = any(ConstCase c).getValue(_) // TODO } -from ControlFlowNode n, string s +from ControlFlowNode n, ExprParent astnode, string s where - // TODO: exists(n.getASuccessor()) and shouldBeDeadEnd(n) and s = "expected dead end" - not exists(n.getASuccessor()) and not shouldBeDeadEnd(n) and s = "unexpected dead end" -select n, n.getPrimaryQlClasses(), s + astnode = n.getAstNode() and + // TODO: exists(n.getASuccessor()) and shouldBeDeadEnd(n.getAstNode()) and s = "expected dead end" + not exists(n.getASuccessor()) and + not shouldBeDeadEnd(astnode) and + s = "unexpected dead end" +select n, astnode.getPrimaryQlClasses(), s diff --git a/java/ql/test/library-tests/controlflow/basic/bbStmts.ql b/java/ql/test/library-tests/controlflow/basic/bbStmts.ql index 89a32acf4482..3b16a3844b56 100644 --- a/java/ql/test/library-tests/controlflow/basic/bbStmts.ql +++ b/java/ql/test/library-tests/controlflow/basic/bbStmts.ql @@ -3,5 +3,5 @@ import default from BasicBlock b, ControlFlowNode n, int i where b.getNode(i) = n and - b.getFile().(CompilationUnit).fromSource() + b.getEnclosingCallable().getFile().(CompilationUnit).fromSource() select b, i, n diff --git a/java/ql/test/library-tests/controlflow/basic/strictDominance.ql b/java/ql/test/library-tests/controlflow/basic/strictDominance.ql index 2d366a4f3727..10529b6d5c86 100644 --- a/java/ql/test/library-tests/controlflow/basic/strictDominance.ql +++ b/java/ql/test/library-tests/controlflow/basic/strictDominance.ql @@ -2,5 +2,5 @@ import default import semmle.code.java.controlflow.Dominance from Stmt pre, Stmt post -where strictlyDominates(pre, post) +where strictlyDominates(pre.getControlFlowNode(), post.getControlFlowNode()) select pre, post diff --git a/java/ql/test/library-tests/controlflow/basic/strictPostDominance.ql b/java/ql/test/library-tests/controlflow/basic/strictPostDominance.ql index 9948718fc83b..99268a03c4be 100644 --- a/java/ql/test/library-tests/controlflow/basic/strictPostDominance.ql +++ b/java/ql/test/library-tests/controlflow/basic/strictPostDominance.ql @@ -2,5 +2,5 @@ import default import semmle.code.java.controlflow.Dominance from Stmt pre, Stmt post -where strictlyPostDominates(post, pre) +where strictlyPostDominates(post.getControlFlowNode(), pre.getControlFlowNode()) select post, pre diff --git a/java/ql/test/library-tests/controlflow/dominance/dominanceBad.ql b/java/ql/test/library-tests/controlflow/dominance/dominanceBad.ql index 26d33d9d07bd..41b23313ec8e 100644 --- a/java/ql/test/library-tests/controlflow/dominance/dominanceBad.ql +++ b/java/ql/test/library-tests/controlflow/dominance/dominanceBad.ql @@ -4,6 +4,6 @@ import semmle.code.java.controlflow.Dominance from IfStmt i, BlockStmt b where b = i.getThen() and - dominates(i.getThen(), b) and - dominates(i.getElse(), b) + dominates(i.getThen().getControlFlowNode(), b.getControlFlowNode()) and + dominates(i.getElse().getControlFlowNode(), b.getControlFlowNode()) select i, b diff --git a/java/ql/test/library-tests/controlflow/dominance/dominanceWrong.ql b/java/ql/test/library-tests/controlflow/dominance/dominanceWrong.ql index 298e0752ee41..5ee23224d5f9 100644 --- a/java/ql/test/library-tests/controlflow/dominance/dominanceWrong.ql +++ b/java/ql/test/library-tests/controlflow/dominance/dominanceWrong.ql @@ -17,5 +17,5 @@ predicate dominanceCounterExample(ControlFlowNode entry, ControlFlowNode dom, Co from Callable c, ControlFlowNode dom, ControlFlowNode node where (strictlyDominates(dom, node) or bbStrictlyDominates(dom, node)) and - dominanceCounterExample(c.getBody(), dom, node) + dominanceCounterExample(c.getBody().getControlFlowNode(), dom, node) select c, dom, node diff --git a/java/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql b/java/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql index b5bdf6889967..9a0d1b0c4bba 100644 --- a/java/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql +++ b/java/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql @@ -3,14 +3,14 @@ import default import semmle.code.java.controlflow.Dominance ControlFlowNode reachableIn(Method func) { - result = func.getBody() or + result = func.getBody().getControlFlowNode() or result = reachableIn(func).getASuccessor() } from Method func, ControlFlowNode entry, ControlFlowNode node where - func.getBody() = entry and + func.getBody().getControlFlowNode() = entry and reachableIn(func) = node and entry != node and - not strictlyDominates(func.getBody(), node) + not strictlyDominates(func.getBody().getControlFlowNode(), node) select func, node diff --git a/java/ql/test/library-tests/controlflow/dominance/dominatorExists.ql b/java/ql/test/library-tests/controlflow/dominance/dominatorExists.ql index 34469a686b14..220e4f275d54 100644 --- a/java/ql/test/library-tests/controlflow/dominance/dominatorExists.ql +++ b/java/ql/test/library-tests/controlflow/dominance/dominatorExists.ql @@ -4,13 +4,13 @@ import semmle.code.java.controlflow.Dominance /** transitive dominance */ ControlFlowNode reachableIn(Method func) { - result = func.getBody() or + result = func.getBody().getControlFlowNode() or result = reachableIn(func).getASuccessor() } from Method func, ControlFlowNode node where node = reachableIn(func) and - node != func.getBody() and + node != func.getBody().getControlFlowNode() and not iDominates(_, node) select func, node diff --git a/java/ql/test/library-tests/controlflow/paths/paths.ql b/java/ql/test/library-tests/controlflow/paths/paths.ql index 389c46a48f59..33e51acef784 100644 --- a/java/ql/test/library-tests/controlflow/paths/paths.ql +++ b/java/ql/test/library-tests/controlflow/paths/paths.ql @@ -5,7 +5,7 @@ class PathTestConf extends ActionConfiguration { PathTestConf() { this = "PathTestConf" } override predicate isAction(ControlFlowNode node) { - node.(MethodCall).getMethod().hasName("action") + node.asExpr().(MethodCall).getMethod().hasName("action") } } From 634b679e5213a521a78bbeb6d1571e62c1591930 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Nov 2024 16:13:14 +0000 Subject: [PATCH 07/16] Fix more compilation errors in tests --- java/ql/consistency-queries/cfgDeadEnds.ql | 1 - .../semmle/code/java/controlflow/BasicBlocks.qll | 4 ++++ java/ql/lib/semmle/code/java/controlflow/Paths.qll | 4 ---- .../semmle/code/java/security/PathSanitizer.qll | 10 +++++++--- .../Comparison/UselessComparisonTest.qll | 2 +- .../DoubleCheckedLockingWithInitRace.ql | 2 +- .../Likely Bugs/Concurrency/LazyInitStaticField.ql | 4 ++-- .../src/Likely Bugs/Concurrency/UnreleasedLock.ql | 14 +++++++------- .../Termination/ConstantLoopCondition.ql | 4 ++-- .../Security/CWE/CWE-833/LockOrderInconsistency.ql | 4 ++-- .../Declarations/BreakInSwitchCase.ql | 2 +- .../Declarations/Common.qll | 6 +++--- .../CWE/CWE-094/SpringViewManipulationLib.qll | 2 +- 13 files changed, 31 insertions(+), 28 deletions(-) diff --git a/java/ql/consistency-queries/cfgDeadEnds.ql b/java/ql/consistency-queries/cfgDeadEnds.ql index 2d5d23d92c43..817d8e858c63 100644 --- a/java/ql/consistency-queries/cfgDeadEnds.ql +++ b/java/ql/consistency-queries/cfgDeadEnds.ql @@ -1,6 +1,5 @@ import java -// import semmle.code.java.ControlFlowGraph predicate shouldBeDeadEnd(ExprParent n) { n instanceof BreakStmt and n.getFile().isKotlinSourceFile() // TODO or diff --git a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll index 3fae0f0b4d27..7881b471a6a4 100644 --- a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll @@ -66,3 +66,7 @@ class BasicBlock extends ControlFlowNode { /** Holds if this basic block post-dominates `node`. (This is reflexive.) */ predicate bbPostDominates(BasicBlock node) { bbPostDominates(this, node) } } + +class ExitBlock extends BasicBlock { + ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode } +} diff --git a/java/ql/lib/semmle/code/java/controlflow/Paths.qll b/java/ql/lib/semmle/code/java/controlflow/Paths.qll index 1fb36b9be369..5a06a3a1ee5d 100644 --- a/java/ql/lib/semmle/code/java/controlflow/Paths.qll +++ b/java/ql/lib/semmle/code/java/controlflow/Paths.qll @@ -43,10 +43,6 @@ private predicate callAlwaysPerformsAction(Call call, ActionConfiguration conf) ) } -private class ExitBlock extends BasicBlock { - ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode } -} - /** Holds if an action dominates the exit of the callable. */ private predicate actionDominatesExit(Callable callable, ActionConfiguration conf) { exists(ExitBlock exit | diff --git a/java/ql/lib/semmle/code/java/security/PathSanitizer.qll b/java/ql/lib/semmle/code/java/security/PathSanitizer.qll index 77803e3e27dc..e841eb598cc3 100644 --- a/java/ql/lib/semmle/code/java/security/PathSanitizer.qll +++ b/java/ql/lib/semmle/code/java/security/PathSanitizer.qll @@ -29,15 +29,19 @@ private module ValidationMethod { */ private predicate validationMethod(Method m, int arg) { exists( - Guard g, SsaImplicitInit var, ControlFlowNode exit, ControlFlowNode normexit, boolean branch + Guard g, SsaImplicitInit var, ControlFlow::ExitNode exit, ControlFlowNode normexit, + boolean branch | validationGuard(g, var.getAUse(), branch) and var.isParameterDefinition(m.getParameter(arg)) and - exit = m and + exit.getEnclosingCallable() = m and normexit.getANormalSuccessor() = exit and 1 = strictcount(ControlFlowNode n | n.getANormalSuccessor() = exit) | - g.(ConditionNode).getABranchSuccessor(branch) = exit or + exists(ConditionNode conditionNode | + g = conditionNode.getCondition() and conditionNode.getABranchSuccessor(branch) = exit + ) + or g.controls(normexit.getBasicBlock(), branch) ) } diff --git a/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll b/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll index e0029ffeba26..2933ae5305e9 100644 --- a/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll +++ b/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll @@ -30,7 +30,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) { ConditionBlock cb, SsaVariable v, BinaryExpr cond, boolean condIsTrue, int k1, int k2, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 | - s1 = cond and + s1.getCondition() = cond and cb.getCondition() = cond and cond.hasOperands(v.getAUse(), c1) and c1.getIntValue() = k1 and diff --git a/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLockingWithInitRace.ql b/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLockingWithInitRace.ql index 241825c092e6..17b9fc93d211 100644 --- a/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLockingWithInitRace.ql +++ b/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLockingWithInitRace.ql @@ -36,7 +36,7 @@ where doubleCheckedLocking(if1, if2, sync, f) and a.getEnclosingStmt().getEnclosingStmt*() = if2.getThen() and se.getEnclosingStmt().getEnclosingStmt*() = sync.getBlock() and - a.(ControlFlowNode).getASuccessor+() = se and + a.getControlFlowNode().getASuccessor+().asExpr() = se and a.getDest().(FieldAccess).getField() = f select a, "Potential race condition. This assignment to $@ is visible to other threads before the subsequent statements are executed.", diff --git a/java/ql/src/Likely Bugs/Concurrency/LazyInitStaticField.ql b/java/ql/src/Likely Bugs/Concurrency/LazyInitStaticField.ql index 757da40c6e6e..13f4ef7c451c 100644 --- a/java/ql/src/Likely Bugs/Concurrency/LazyInitStaticField.ql +++ b/java/ql/src/Likely Bugs/Concurrency/LazyInitStaticField.ql @@ -64,12 +64,12 @@ class ValidSynchStmt extends Stmt { exists(MethodCall lockAction | lockAction.getQualifier() = lockField.getAnAccess() and lockAction.getMethod().getName() = "lock" and - dominates(lockAction, this) + dominates(lockAction.getControlFlowNode(), this.getControlFlowNode()) ) and exists(MethodCall unlockAction | unlockAction.getQualifier() = lockField.getAnAccess() and unlockAction.getMethod().getName() = "unlock" and - postDominates(unlockAction, this) + postDominates(unlockAction.getControlFlowNode(), this.getControlFlowNode()) ) ) } diff --git a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql index 4efaf4f9820b..d46acc6aee06 100644 --- a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql +++ b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql @@ -59,11 +59,11 @@ class LockType extends RefType { } predicate lockBlock(LockType t, BasicBlock b, int locks) { - locks = strictcount(int i | b.getNode(i) = t.getLockAccess()) + locks = strictcount(int i | b.getNode(i).asExpr() = t.getLockAccess()) } predicate unlockBlock(LockType t, BasicBlock b, int unlocks) { - unlocks = strictcount(int i | b.getNode(i) = t.getUnlockAccess()) + unlocks = strictcount(int i | b.getNode(i).asExpr() = t.getUnlockAccess()) } /** @@ -90,11 +90,11 @@ predicate failedLock(LockType t, BasicBlock lockblock, BasicBlock exblock) { exists(ControlFlowNode lock | lock = lockblock.getLastNode() and ( - lock = t.getLockAccess() + lock.asExpr() = t.getLockAccess() or exists(SsaExplicitUpdate lockbool | // Using the value of `t.getLockAccess()` ensures that it is a `tryLock` call. - lock = lockbool.getAUse() and + lock.asExpr() = lockbool.getAUse() and lockbool.getDefiningExpr().(VariableAssign).getSource() = t.getLockAccess() ) ) and @@ -147,12 +147,12 @@ predicate blockIsLocked(LockType t, BasicBlock src, BasicBlock b, int locks) { ) } -from Callable c, LockType t, BasicBlock src, BasicBlock exit, MethodCall lock +from Callable c, LockType t, BasicBlock src, ExitBlock exit, MethodCall lock where // Restrict results to those methods that actually attempt to unlock. t.getUnlockAccess().getEnclosingCallable() = c and blockIsLocked(t, src, exit, _) and - exit.getLastNode() = c and - lock = src.getANode() and + exit.getEnclosingCallable() = c and + lock = src.getANode().asExpr() and lock = t.getLockAccess() select lock, "This lock might not be unlocked or might be locked more times than it is unlocked." diff --git a/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql b/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql index 7dc893edaef9..9cf8ab9b61fd 100644 --- a/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql +++ b/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql @@ -61,7 +61,7 @@ predicate mainLoopCondition(LoopStmt loop, Expr cond) { else loopReentry = cond | last.getEnclosingStmt().getEnclosingStmt*() = loop.getBody() and - last.getASuccessor().(Expr).getParent*() = loopReentry + last.getASuccessor().asExpr().getParent*() = loopReentry ) } @@ -75,7 +75,7 @@ where // None of the ssa variables in `cond` are updated inside the loop. forex(SsaVariable ssa, VarRead use | ssa.getAUse() = use and use.getParent*() = cond | not ssa.getCfgNode().getEnclosingStmt().getEnclosingStmt*() = loop or - ssa.getCfgNode().(Expr).getParent*() = loop.(ForStmt).getAnInit() + ssa.getCfgNode().asExpr().getParent*() = loop.(ForStmt).getAnInit() ) and // And `cond` does not use method calls, field reads, or array reads. not exists(MethodCall ma | ma.getParent*() = cond) and diff --git a/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql b/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql index 4e9857f3b938..899218838b9a 100644 --- a/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql +++ b/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql @@ -80,8 +80,8 @@ predicate badReentrantLockOrder(MethodCall first, MethodCall second, MethodCall otherSecond = v1.getLockAction() and second = v2.getLockAction() and otherFirst = v2.getLockAction() and - first.(ControlFlowNode).getASuccessor+() = second and - otherFirst.(ControlFlowNode).getASuccessor+() = otherSecond + first.getControlFlowNode().getASuccessor+() = second.getControlFlowNode() and + otherFirst.getControlFlowNode().getASuccessor+() = otherSecond.getControlFlowNode() | v1 != v2 ) diff --git a/java/ql/src/Violations of Best Practice/Declarations/BreakInSwitchCase.ql b/java/ql/src/Violations of Best Practice/Declarations/BreakInSwitchCase.ql index 312a77878fe6..2fe9d3cc6727 100644 --- a/java/ql/src/Violations of Best Practice/Declarations/BreakInSwitchCase.ql +++ b/java/ql/src/Violations of Best Practice/Declarations/BreakInSwitchCase.ql @@ -17,7 +17,7 @@ import Common from SwitchStmt s, Stmt c where c = s.getACase() and - not c.(ControlFlowNode).getASuccessor() instanceof SwitchCase and + not c.getControlFlowNode().getASuccessor().asStmt() instanceof SwitchCase and not s.(Annotatable).suppressesWarningsAbout("fallthrough") and mayDropThroughWithoutComment(s, c) select c, diff --git a/java/ql/src/Violations of Best Practice/Declarations/Common.qll b/java/ql/src/Violations of Best Practice/Declarations/Common.qll index 0f95df4b5c40..9211c4b0f290 100644 --- a/java/ql/src/Violations of Best Practice/Declarations/Common.qll +++ b/java/ql/src/Violations of Best Practice/Declarations/Common.qll @@ -24,15 +24,15 @@ predicate switchCaseControlFlowPlus(SwitchStmt switch, BasicBlock b1, BasicBlock exists(BasicBlock mid | switchCaseControlFlowPlus(switch, mid, b2) and switchCaseControlFlow(switch, b1, mid) and - not mid.getFirstNode() = switch.getACase() + not mid.getFirstNode().asStmt() = switch.getACase() ) } predicate mayDropThroughWithoutComment(SwitchStmt switch, Stmt switchCase) { switchCase = switch.getACase() and exists(Stmt other, BasicBlock b1, BasicBlock b2 | - b1.getFirstNode() = switchCase and - b2.getFirstNode() = other and + b1.getFirstNode().asStmt() = switchCase and + b2.getFirstNode().asStmt() = other and switchCaseControlFlowPlus(switch, b1, b2) and other = switch.getACase() and not fallThroughCommented(other) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll index 256947a2dc79..0771db5ee32b 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-094/SpringViewManipulationLib.qll @@ -55,7 +55,7 @@ module SpringViewManipulationConfig implements DataFlow::ConfigSig { // a = "redirect:" + taint` // ``` exists(AddExpr e, StringLiteral sl | - node.asExpr() = e.getControlFlowNode().getASuccessor*() and + node.asExpr() = e.getControlFlowNode().getASuccessor*().asExpr() and sl = e.getLeftOperand*() and sl.getValue().matches(["redirect:%", "ajaxredirect:%", "forward:%"]) ) From a023fd9bb5c5a7ea72788d835247f3f316226eb3 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Nov 2024 16:15:23 +0000 Subject: [PATCH 08/16] Fix another compilation error --- java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index 01726ad5a1d7..93f00ed815c2 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -1395,7 +1395,7 @@ private module ControlFlowGraphImpl { switchExpr = switch.(SwitchStmt).getExpr() or switchExpr = switch.(SwitchExpr).getExpr() | // From the entry point control is transferred first to the expression... - n = switch.(AstNode).getCFGNode() and + (n.asStmt() = switch or n.asExpr() = switch) and result = first(switchExpr) and completion = NormalCompletion() or From ae12d27b56bbf66f7da603231396324b4f4270fe Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Nov 2024 16:15:53 +0000 Subject: [PATCH 09/16] Update test expectations --- .../controlflow/basic/bbStrictDominance.expected | 4 ++-- .../test/library-tests/controlflow/basic/bbSuccessor.expected | 4 ++-- .../library-tests/controlflow/dominance/dominator.expected | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/ql/test/library-tests/controlflow/basic/bbStrictDominance.expected b/java/ql/test/library-tests/controlflow/basic/bbStrictDominance.expected index 13382ba1fba0..8440209d0a45 100644 --- a/java/ql/test/library-tests/controlflow/basic/bbStrictDominance.expected +++ b/java/ql/test/library-tests/controlflow/basic/bbStrictDominance.expected @@ -1,4 +1,4 @@ -| Test.java:4:21:76:2 | { ... } | Test.java:4:14:4:17 | test | +| Test.java:4:21:76:2 | { ... } | Test.java:4:14:4:17 | Exit | | Test.java:4:21:76:2 | { ... } | Test.java:11:14:14:3 | { ... } | | Test.java:4:21:76:2 | { ... } | Test.java:14:10:16:3 | { ... } | | Test.java:4:21:76:2 | { ... } | Test.java:18:3:18:8 | ; | @@ -20,7 +20,7 @@ | Test.java:4:21:76:2 | { ... } | Test.java:60:12:62:5 | { ... } | | Test.java:4:21:76:2 | { ... } | Test.java:63:9:66:4 | { ... } | | Test.java:4:21:76:2 | { ... } | Test.java:70:3:70:9 | ; | -| Test.java:18:3:18:8 | ; | Test.java:4:14:4:17 | test | +| Test.java:18:3:18:8 | ; | Test.java:4:14:4:17 | Exit | | Test.java:18:3:18:8 | ; | Test.java:22:4:22:10 | ; | | Test.java:18:3:18:8 | ; | Test.java:24:4:24:10 | return ... | | Test.java:18:3:18:8 | ; | Test.java:30:15:33:3 | { ... } | diff --git a/java/ql/test/library-tests/controlflow/basic/bbSuccessor.expected b/java/ql/test/library-tests/controlflow/basic/bbSuccessor.expected index 0cf2c96d0225..0886f784fd9a 100644 --- a/java/ql/test/library-tests/controlflow/basic/bbSuccessor.expected +++ b/java/ql/test/library-tests/controlflow/basic/bbSuccessor.expected @@ -6,7 +6,7 @@ | Test.java:18:3:18:8 | ; | Test.java:24:4:24:10 | return ... | | Test.java:22:4:22:10 | ; | Test.java:30:15:33:3 | { ... } | | Test.java:22:4:22:10 | ; | Test.java:35:3:35:9 | ; | -| Test.java:24:4:24:10 | return ... | Test.java:4:14:4:17 | test | +| Test.java:24:4:24:10 | return ... | Test.java:4:14:4:17 | Exit | | Test.java:30:15:33:3 | { ... } | Test.java:35:3:35:9 | ; | | Test.java:35:3:35:9 | ; | Test.java:38:9:38:9 | x | | Test.java:38:9:38:9 | x | Test.java:38:16:41:3 | { ... } | @@ -27,4 +27,4 @@ | Test.java:57:15:60:5 | { ... } | Test.java:70:3:70:9 | ; | | Test.java:60:12:62:5 | { ... } | Test.java:54:26:54:26 | j | | Test.java:63:9:66:4 | { ... } | Test.java:54:26:54:26 | j | -| Test.java:70:3:70:9 | ; | Test.java:4:14:4:17 | test | +| Test.java:70:3:70:9 | ; | Test.java:4:14:4:17 | Exit | diff --git a/java/ql/test/library-tests/controlflow/dominance/dominator.expected b/java/ql/test/library-tests/controlflow/dominance/dominator.expected index e0f1596e42b1..de43e6721e64 100644 --- a/java/ql/test/library-tests/controlflow/dominance/dominator.expected +++ b/java/ql/test/library-tests/controlflow/dominance/dominator.expected @@ -27,7 +27,7 @@ | Test.java:14:18:14:18 | y | Test.java:14:14:14:18 | ... + ... | | Test.java:17:3:17:12 | if (...) | Test.java:17:7:17:7 | x | | Test.java:17:7:17:7 | x | Test.java:17:11:17:11 | 0 | -| Test.java:17:7:17:11 | ... < ... | Test.java:2:6:2:9 | test | +| Test.java:17:7:17:11 | ... < ... | Test.java:2:6:2:9 | Exit | | Test.java:17:7:17:11 | ... < ... | Test.java:18:4:18:10 | ; | | Test.java:17:7:17:11 | ... < ... | Test.java:20:11:20:11 | z | | Test.java:17:11:17:11 | 0 | Test.java:17:7:17:11 | ... < ... | @@ -163,7 +163,7 @@ | Test.java:83:9:83:9 | c | Test.java:83:5:83:9 | ...=... | | Test.java:85:4:85:15 | if (...) | Test.java:85:8:85:8 | a | | Test.java:85:8:85:8 | a | Test.java:85:13:85:14 | 10 | -| Test.java:85:8:85:14 | ... == ... | Test.java:74:6:74:10 | test2 | +| Test.java:85:8:85:14 | ... == ... | Test.java:74:6:74:10 | Exit | | Test.java:85:8:85:14 | ... == ... | Test.java:86:5:86:10 | break | | Test.java:85:8:85:14 | ... == ... | Test.java:87:4:87:15 | if (...) | | Test.java:85:13:85:14 | 10 | Test.java:85:8:85:14 | ... == ... | From d854396cca45e586c7a226fc59c3091f2cd7e1d7 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 14 Nov 2024 00:56:26 +0000 Subject: [PATCH 10/16] Fix switchexpr control flow error --- java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index 93f00ed815c2..66ea239cf6b0 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -86,7 +86,7 @@ private import controlflow.internal.SwitchCases module ControlFlow { private predicate hasControlFlow(Expr e) { - not e.getEnclosingStmt() instanceof ConstCase and + not exists(ConstCase cc | e = cc.getValue(_)) and not e.getParent*() instanceof Annotation and not e instanceof TypeAccess and not e instanceof ArrayTypeAccess and From 88fb0d59ada25262ec19dd21aa72ec85f001c383 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 14 Nov 2024 08:51:22 +0100 Subject: [PATCH 11/16] Java: fix UnreachabaleBasicBlock Fix is taken from https://github.com/github/codeql/pull/711/files#diff-24d21bcfe63e29a6ef28151d9bd8eeaa610565c3ed7b846de1eb5fb0be661ae5 It is not clear why anotations are left out. --- .../code/java/controlflow/UnreachableBlocks.qll | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll index 3145371561a7..a057fbabae31 100644 --- a/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll @@ -207,14 +207,12 @@ class UnreachableBasicBlock extends BasicBlock { conditionBlock.controls(this, constant.booleanNot()) ) or - // This block is not reachable in the CFG, and is not a callable, a body of a callable, an - // expression in an annotation, an expression in an assert statement, or a catch clause. + // This block is not reachable in the CFG, and is not the entrypoint in a callable, an + // expression in an assert statement, or a catch clause. forall(BasicBlock bb | bb = this.getABBPredecessor() | bb instanceof UnreachableBasicBlock) and - not exists(Callable c | c.getBody() = this) and - not this instanceof Callable and - not exists(Annotation a | a.getAChildExpr*() = this) and - not this.(Expr).getEnclosingStmt() instanceof AssertStmt and - not this instanceof CatchClause + not exists(Callable c | c.getBody().getControlFlowNode() = this.getFirstNode()) and + not exists(AssertStmt a | a = this.getFirstNode().asExpr().getEnclosingStmt()) and + not this.getFirstNode().asStmt() instanceof CatchClause or // Switch statements with a constant comparison expression may have unreachable cases. exists(ConstSwitchStmt constSwitchStmt, BasicBlock failingCaseBlock | @@ -223,7 +221,7 @@ class UnreachableBasicBlock extends BasicBlock { // Not accessible from the successful case not constSwitchStmt.getMatchingCase().getBasicBlock().getABBSuccessor*() = failingCaseBlock and // Blocks dominated by the failing case block are unreachable - constSwitchStmt.getAFailingCase().getBasicBlock().bbDominates(this) + failingCaseBlock.bbDominates(this) ) } } From bcc6523945935d3e4d95396d49b2aeeecafe1a1e Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 14 Nov 2024 10:32:22 +0000 Subject: [PATCH 12/16] Accept trivial test changes --- .../controlflow/basic/bbStmts.expected | 4 ++-- .../MultiCatch/MultiCatchControlFlow.expected | 10 ++++----- .../pattern-instanceof/cfg.expected | 14 ++++++------ .../library-tests/pattern-instanceof/cfg.ql | 2 +- .../pattern-switch/cfg/test.expected | 22 +++++++++---------- .../library-tests/pattern-switch/cfg/test.ql | 2 +- .../CloseReaderTest/TestSucc.expected | 6 ++--- .../successors/CloseReaderTest/TestSucc.ql | 2 +- .../LoopVarReadTest/TestSucc.expected | 4 ++-- .../successors/LoopVarReadTest/TestSucc.ql | 2 +- .../successors/SaveFileTest/TestSucc.expected | 6 ++--- .../successors/SaveFileTest/TestSucc.ql | 2 +- .../successors/SchackTest/TestSucc.expected | 12 +++++----- .../successors/SchackTest/TestSucc.ql | 2 +- .../successors/TestBreak/TestSucc.expected | 8 +++---- .../successors/TestBreak/TestSucc.ql | 2 +- .../TestContinue/FalseSuccessors.expected | 2 +- .../successors/TestContinue/TestSucc.expected | 6 ++--- .../successors/TestContinue/TestSucc.ql | 2 +- .../TestDeclarations/TestSucc.expected | 6 ++--- .../successors/TestDeclarations/TestSucc.ql | 2 +- .../successors/TestFinally/TestSucc.expected | 10 ++++----- .../successors/TestFinally/TestSucc.ql | 2 +- .../TestSucc.expected | 6 ++--- .../TestFinallyBreakContinue/TestSucc.ql | 2 +- .../TestLoopBranch/TestSucc.expected | 14 ++++++------ .../successors/TestLoopBranch/TestSucc.ql | 2 +- .../successors/TestThrow/TestSucc.expected | 18 +++++++-------- .../successors/TestThrow/TestSucc.ql | 2 +- .../successors/TestThrow2/TestSucc.expected | 6 ++--- .../successors/TestThrow2/TestSucc.ql | 2 +- .../successors/TestTryCatch/TestSucc.expected | 6 ++--- .../successors/TestTryCatch/TestSucc.ql | 2 +- .../TestTryWithResources/TestSucc.expected | 4 ++-- .../TestTryWithResources/TestSucc.ql | 2 +- 35 files changed, 98 insertions(+), 98 deletions(-) diff --git a/java/ql/test/library-tests/controlflow/basic/bbStmts.expected b/java/ql/test/library-tests/controlflow/basic/bbStmts.expected index e1a8d6b4ade5..0fbf3623f089 100644 --- a/java/ql/test/library-tests/controlflow/basic/bbStmts.expected +++ b/java/ql/test/library-tests/controlflow/basic/bbStmts.expected @@ -1,7 +1,7 @@ | Test.java:3:14:3:17 | { ... } | 0 | Test.java:3:14:3:17 | { ... } | | Test.java:3:14:3:17 | { ... } | 1 | Test.java:3:14:3:17 | super(...) | -| Test.java:3:14:3:17 | { ... } | 2 | Test.java:3:14:3:17 | Test | -| Test.java:4:14:4:17 | test | 0 | Test.java:4:14:4:17 | test | +| Test.java:3:14:3:17 | { ... } | 2 | Test.java:3:14:3:17 | Exit | +| Test.java:4:14:4:17 | Exit | 0 | Test.java:4:14:4:17 | Exit | | Test.java:4:21:76:2 | { ... } | 0 | Test.java:4:21:76:2 | { ... } | | Test.java:4:21:76:2 | { ... } | 1 | Test.java:5:3:5:12 | var ...; | | Test.java:4:21:76:2 | { ... } | 2 | Test.java:5:11:5:11 | 0 | diff --git a/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected b/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected index 40a8e58c4fea..a849ab5392d5 100644 --- a/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected +++ b/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected @@ -1,4 +1,4 @@ -| MultiCatch.java:6:14:6:23 | super(...) | MultiCatch.java:6:14:6:23 | MultiCatch | +| MultiCatch.java:6:14:6:23 | super(...) | MultiCatch.java:6:14:6:23 | Exit | | MultiCatch.java:6:14:6:23 | { ... } | MultiCatch.java:6:14:6:23 | super(...) | | MultiCatch.java:8:2:20:2 | { ... } | MultiCatch.java:9:3:19:3 | try ... | | MultiCatch.java:9:3:19:3 | try ... | MultiCatch.java:10:3:15:3 | { ... } | @@ -16,7 +16,7 @@ | MultiCatch.java:17:4:17:4 | e | MultiCatch.java:17:4:17:22 | printStackTrace(...) | | MultiCatch.java:17:4:17:22 | printStackTrace(...) | MultiCatch.java:18:10:18:10 | e | | MultiCatch.java:17:4:17:23 | ; | MultiCatch.java:17:4:17:4 | e | -| MultiCatch.java:18:4:18:11 | throw ... | MultiCatch.java:7:14:7:23 | multiCatch | +| MultiCatch.java:18:4:18:11 | throw ... | MultiCatch.java:7:14:7:23 | Exit | | MultiCatch.java:18:10:18:10 | e | MultiCatch.java:18:4:18:11 | throw ... | | MultiCatch.java:23:2:33:2 | { ... } | MultiCatch.java:24:3:32:4 | try ... | | MultiCatch.java:24:3:32:4 | try ... | MultiCatch.java:25:3:31:3 | { ... } | @@ -31,12 +31,12 @@ | MultiCatch.java:28:12:28:12 | c | MultiCatch.java:30:10:30:24 | new Exception(...) | | MultiCatch.java:29:5:29:29 | throw ... | MultiCatch.java:31:5:31:37 | catch (...) | | MultiCatch.java:29:11:29:28 | new SQLException(...) | MultiCatch.java:29:5:29:29 | throw ... | -| MultiCatch.java:30:4:30:25 | throw ... | MultiCatch.java:22:14:22:24 | multiCatch2 | +| MultiCatch.java:30:4:30:25 | throw ... | MultiCatch.java:22:14:22:24 | Exit | | MultiCatch.java:30:4:30:25 | throw ... | MultiCatch.java:31:5:31:37 | catch (...) | | MultiCatch.java:30:10:30:24 | new Exception(...) | MultiCatch.java:30:4:30:25 | throw ... | | MultiCatch.java:31:5:31:37 | catch (...) | MultiCatch.java:31:36:31:36 | e | | MultiCatch.java:31:36:31:36 | e | MultiCatch.java:32:3:32:4 | { ... } | -| MultiCatch.java:32:3:32:4 | { ... } | MultiCatch.java:22:14:22:24 | multiCatch2 | +| MultiCatch.java:32:3:32:4 | { ... } | MultiCatch.java:22:14:22:24 | Exit | | MultiCatch.java:36:2:42:2 | { ... } | MultiCatch.java:37:3:41:4 | try ... | | MultiCatch.java:37:3:41:4 | try ... | MultiCatch.java:38:3:40:3 | { ... } | | MultiCatch.java:38:3:40:3 | { ... } | MultiCatch.java:39:10:39:26 | new IOException(...) | @@ -45,4 +45,4 @@ | MultiCatch.java:39:10:39:26 | new IOException(...) | MultiCatch.java:40:5:40:22 | catch (...) | | MultiCatch.java:40:5:40:22 | catch (...) | MultiCatch.java:40:21:40:21 | e | | MultiCatch.java:40:21:40:21 | e | MultiCatch.java:41:3:41:4 | { ... } | -| MultiCatch.java:41:3:41:4 | { ... } | MultiCatch.java:35:14:35:26 | ordinaryCatch | +| MultiCatch.java:41:3:41:4 | { ... } | MultiCatch.java:35:14:35:26 | Exit | diff --git a/java/ql/test/library-tests/pattern-instanceof/cfg.expected b/java/ql/test/library-tests/pattern-instanceof/cfg.expected index 29de1e4a3a8a..b6caebd532a6 100644 --- a/java/ql/test/library-tests/pattern-instanceof/cfg.expected +++ b/java/ql/test/library-tests/pattern-instanceof/cfg.expected @@ -1,4 +1,4 @@ -| Test.java:1:14:1:17 | super(...) | Test.java:1:14:1:17 | Test | +| Test.java:1:14:1:17 | super(...) | Test.java:1:14:1:17 | Exit | | Test.java:1:14:1:17 | { ... } | Test.java:1:14:1:17 | super(...) | | Test.java:3:40:20:3 | { ... } | Test.java:5:5:5:34 | var ...; | | Test.java:5:5:5:34 | var ...; | Test.java:5:26:5:33 | source(...) | @@ -29,7 +29,7 @@ | Test.java:11:12:11:12 | s | Test.java:11:7:11:13 | sink(...) | | Test.java:14:5:14:92 | if (...) | Test.java:14:9:14:9 | o | | Test.java:14:9:14:9 | o | Test.java:14:9:14:91 | ...instanceof... | -| Test.java:14:9:14:91 | ...instanceof... | Test.java:3:22:3:25 | test | +| Test.java:14:9:14:91 | ...instanceof... | Test.java:3:22:3:25 | Exit | | Test.java:14:9:14:91 | ...instanceof... | Test.java:14:41:14:47 | tainted | | Test.java:14:22:14:91 | Outer(...) | Test.java:14:94:18:5 | { ... } | | Test.java:14:28:14:67 | Inner(...) | Test.java:14:77:14:90 | alsoNotTainted | @@ -43,15 +43,15 @@ | Test.java:16:7:16:22 | sink(...) | Test.java:17:7:17:27 | ; | | Test.java:16:7:16:23 | ; | Test.java:16:12:16:21 | notTainted | | Test.java:16:12:16:21 | notTainted | Test.java:16:7:16:22 | sink(...) | -| Test.java:17:7:17:26 | sink(...) | Test.java:3:22:3:25 | test | +| Test.java:17:7:17:26 | sink(...) | Test.java:3:22:3:25 | Exit | | Test.java:17:7:17:27 | ; | Test.java:17:12:17:25 | alsoNotTainted | | Test.java:17:12:17:25 | alsoNotTainted | Test.java:17:7:17:26 | sink(...) | | Test.java:22:33:22:53 | { ... } | Test.java:22:42:22:50 | "tainted" | -| Test.java:22:35:22:51 | return ... | Test.java:22:24:22:29 | source | +| Test.java:22:35:22:51 | return ... | Test.java:22:24:22:29 | Exit | | Test.java:22:42:22:50 | "tainted" | Test.java:22:35:22:51 | return ... | -| Test.java:23:40:23:42 | { ... } | Test.java:23:22:23:25 | sink | +| Test.java:23:40:23:42 | { ... } | Test.java:23:22:23:25 | Exit | | Test.java:27:8:27:12 | ...=... | Test.java:27:8:27:12 | ; | -| Test.java:27:8:27:12 | ...=... | Test.java:27:8:27:12 | Outer | +| Test.java:27:8:27:12 | ...=... | Test.java:27:8:27:12 | Exit | | Test.java:27:8:27:12 | ; | Test.java:27:8:27:12 | this | | Test.java:27:8:27:12 | ; | Test.java:27:8:27:12 | this | | Test.java:27:8:27:12 | i | Test.java:27:8:27:12 | ...=... | @@ -61,7 +61,7 @@ | Test.java:27:8:27:12 | this | Test.java:27:8:27:12 | otherField | | Test.java:27:8:27:12 | { ... } | Test.java:27:8:27:12 | super(...) | | Test.java:28:8:28:12 | ...=... | Test.java:28:8:28:12 | ; | -| Test.java:28:8:28:12 | ...=... | Test.java:28:8:28:12 | Inner | +| Test.java:28:8:28:12 | ...=... | Test.java:28:8:28:12 | Exit | | Test.java:28:8:28:12 | ; | Test.java:28:8:28:12 | this | | Test.java:28:8:28:12 | ; | Test.java:28:8:28:12 | this | | Test.java:28:8:28:12 | nonTaintedField | Test.java:28:8:28:12 | ...=... | diff --git a/java/ql/test/library-tests/pattern-instanceof/cfg.ql b/java/ql/test/library-tests/pattern-instanceof/cfg.ql index 0b07e8c4708a..db2cc49bc0b6 100644 --- a/java/ql/test/library-tests/pattern-instanceof/cfg.ql +++ b/java/ql/test/library-tests/pattern-instanceof/cfg.ql @@ -1,5 +1,5 @@ import java from ControlFlowNode cn -where cn.getFile().getBaseName() = "Test.java" +where cn.getLocation().getFile().getBaseName() = "Test.java" select cn, cn.getASuccessor() diff --git a/java/ql/test/library-tests/pattern-switch/cfg/test.expected b/java/ql/test/library-tests/pattern-switch/cfg/test.expected index a63aa7886683..c29059faf33e 100644 --- a/java/ql/test/library-tests/pattern-switch/cfg/test.expected +++ b/java/ql/test/library-tests/pattern-switch/cfg/test.expected @@ -1,6 +1,6 @@ -| Exhaustive.java:1:14:1:23 | super(...) | Exhaustive.java:1:14:1:23 | Exhaustive | +| Exhaustive.java:1:14:1:23 | super(...) | Exhaustive.java:1:14:1:23 | Exit | | Exhaustive.java:1:14:1:23 | { ... } | Exhaustive.java:1:14:1:23 | super(...) | -| Exhaustive.java:3:8:3:8 | super(...) | Exhaustive.java:3:8:3:8 | E | +| Exhaustive.java:3:8:3:8 | super(...) | Exhaustive.java:3:8:3:8 | Exit | | Exhaustive.java:3:8:3:8 | { ... } | Exhaustive.java:3:8:3:8 | super(...) | | Exhaustive.java:3:8:3:8 | { ... } | Exhaustive.java:3:12:3:12 | ; | | Exhaustive.java:3:12:3:12 | ...=... | Exhaustive.java:3:15:3:15 | ; | @@ -9,12 +9,12 @@ | Exhaustive.java:3:15:3:15 | ...=... | Exhaustive.java:3:18:3:18 | ; | | Exhaustive.java:3:15:3:15 | ; | Exhaustive.java:3:15:3:15 | new E(...) | | Exhaustive.java:3:15:3:15 | new E(...) | Exhaustive.java:3:15:3:15 | ...=... | -| Exhaustive.java:3:18:3:18 | ...=... | Exhaustive.java:3:8:3:8 | | +| Exhaustive.java:3:18:3:18 | ...=... | Exhaustive.java:3:8:3:8 | Exit | | Exhaustive.java:3:18:3:18 | ; | Exhaustive.java:3:18:3:18 | new E(...) | | Exhaustive.java:3:18:3:18 | new E(...) | Exhaustive.java:3:18:3:18 | ...=... | -| Exhaustive.java:5:15:5:15 | super(...) | Exhaustive.java:5:15:5:15 | X | +| Exhaustive.java:5:15:5:15 | super(...) | Exhaustive.java:5:15:5:15 | Exit | | Exhaustive.java:5:15:5:15 | { ... } | Exhaustive.java:5:15:5:15 | super(...) | -| Exhaustive.java:6:15:6:15 | super(...) | Exhaustive.java:6:15:6:15 | Y | +| Exhaustive.java:6:15:6:15 | super(...) | Exhaustive.java:6:15:6:15 | Exit | | Exhaustive.java:6:15:6:15 | { ... } | Exhaustive.java:6:15:6:15 | super(...) | | Exhaustive.java:8:47:35:3 | { ... } | Exhaustive.java:11:5:11:14 | switch (...) | | Exhaustive.java:11:5:11:14 | switch (...) | Exhaustive.java:11:13:11:13 | o | @@ -50,10 +50,10 @@ | Exhaustive.java:30:13:30:13 | i | Exhaustive.java:31:7:31:15 | case | | Exhaustive.java:31:7:31:15 | case | Exhaustive.java:31:14:31:14 | | | Exhaustive.java:31:7:31:15 | case | Exhaustive.java:32:7:32:15 | case | -| Exhaustive.java:31:14:31:14 | | Exhaustive.java:8:22:8:25 | test | +| Exhaustive.java:31:14:31:14 | | Exhaustive.java:8:22:8:25 | Exit | | Exhaustive.java:32:7:32:15 | case | Exhaustive.java:32:14:32:14 | | -| Exhaustive.java:32:14:32:14 | | Exhaustive.java:8:22:8:25 | test | -| Test.java:1:14:1:17 | super(...) | Test.java:1:14:1:17 | Test | +| Exhaustive.java:32:14:32:14 | | Exhaustive.java:8:22:8:25 | Exit | +| Test.java:1:14:1:17 | super(...) | Test.java:1:14:1:17 | Exit | | Test.java:1:14:1:17 | { ... } | Test.java:1:14:1:17 | super(...) | | Test.java:3:41:134:3 | { ... } | Test.java:5:6:5:19 | switch (...) | | Test.java:5:6:5:19 | switch (...) | Test.java:5:14:5:18 | thing | @@ -380,9 +380,9 @@ | Test.java:130:8:130:21 | case | Test.java:130:20:130:20 | | | Test.java:130:8:130:21 | case | Test.java:131:8:131:15 | default | | Test.java:130:20:130:20 | | Test.java:131:8:131:15 | default | -| Test.java:131:8:131:15 | default | Test.java:3:22:3:25 | test | +| Test.java:131:8:131:15 | default | Test.java:3:22:3:25 | Exit | | Test.java:138:8:138:8 | ...=... | Test.java:138:8:138:8 | ; | -| Test.java:138:8:138:8 | ...=... | Test.java:138:8:138:8 | A | +| Test.java:138:8:138:8 | ...=... | Test.java:138:8:138:8 | Exit | | Test.java:138:8:138:8 | ; | Test.java:138:8:138:8 | this | | Test.java:138:8:138:8 | ; | Test.java:138:8:138:8 | this | | Test.java:138:8:138:8 | b | Test.java:138:8:138:8 | ...=... | @@ -392,7 +392,7 @@ | Test.java:138:8:138:8 | this | Test.java:138:8:138:8 | field3 | | Test.java:138:8:138:8 | { ... } | Test.java:138:8:138:8 | super(...) | | Test.java:139:8:139:8 | ...=... | Test.java:139:8:139:8 | ; | -| Test.java:139:8:139:8 | ...=... | Test.java:139:8:139:8 | B | +| Test.java:139:8:139:8 | ...=... | Test.java:139:8:139:8 | Exit | | Test.java:139:8:139:8 | ; | Test.java:139:8:139:8 | this | | Test.java:139:8:139:8 | ; | Test.java:139:8:139:8 | this | | Test.java:139:8:139:8 | field1 | Test.java:139:8:139:8 | ...=... | diff --git a/java/ql/test/library-tests/pattern-switch/cfg/test.ql b/java/ql/test/library-tests/pattern-switch/cfg/test.ql index 4511277ee7da..7e0a85af822a 100644 --- a/java/ql/test/library-tests/pattern-switch/cfg/test.ql +++ b/java/ql/test/library-tests/pattern-switch/cfg/test.ql @@ -1,5 +1,5 @@ import java from ControlFlowNode cn -where cn.getFile().getBaseName() = ["Test.java", "Exhaustive.java"] +where cn.getLocation().getFile().getBaseName() = ["Test.java", "Exhaustive.java"] select cn, cn.getASuccessor() diff --git a/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected b/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected index 63af489090b8..a6f3820334af 100644 --- a/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected +++ b/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected @@ -1,4 +1,4 @@ -| CloseReaderTest.java:8:14:8:28 | super(...) | CloseReaderTest.java:8:14:8:28 | CloseReaderTest | +| CloseReaderTest.java:8:14:8:28 | super(...) | CloseReaderTest.java:8:14:8:28 | Exit | | CloseReaderTest.java:8:14:8:28 | { ... } | CloseReaderTest.java:8:14:8:28 | super(...) | | CloseReaderTest.java:10:2:24:2 | { ... } | CloseReaderTest.java:12:3:13:42 | ; | | CloseReaderTest.java:12:3:12:12 | System.out | CloseReaderTest.java:12:20:12:40 | "Enter password for " | @@ -19,12 +19,12 @@ | CloseReaderTest.java:16:5:16:13 | System.in | CloseReaderTest.java:15:45:16:14 | new InputStreamReader(...) | | CloseReaderTest.java:17:3:23:3 | try ... | CloseReaderTest.java:18:3:20:3 | { ... } | | CloseReaderTest.java:18:3:20:3 | { ... } | CloseReaderTest.java:19:11:19:15 | stdin | -| CloseReaderTest.java:19:4:19:27 | return ... | CloseReaderTest.java:9:23:9:34 | readPassword | +| CloseReaderTest.java:19:4:19:27 | return ... | CloseReaderTest.java:9:23:9:34 | Exit | | CloseReaderTest.java:19:11:19:15 | stdin | CloseReaderTest.java:19:11:19:26 | readLine(...) | | CloseReaderTest.java:19:11:19:26 | readLine(...) | CloseReaderTest.java:19:4:19:27 | return ... | | CloseReaderTest.java:19:11:19:26 | readLine(...) | CloseReaderTest.java:20:5:20:26 | catch (...) | | CloseReaderTest.java:20:5:20:26 | catch (...) | CloseReaderTest.java:20:24:20:25 | ex | | CloseReaderTest.java:20:24:20:25 | ex | CloseReaderTest.java:21:3:23:3 | { ... } | | CloseReaderTest.java:21:3:23:3 | { ... } | CloseReaderTest.java:22:11:22:14 | null | -| CloseReaderTest.java:22:4:22:15 | return ... | CloseReaderTest.java:9:23:9:34 | readPassword | +| CloseReaderTest.java:22:4:22:15 | return ... | CloseReaderTest.java:9:23:9:34 | Exit | | CloseReaderTest.java:22:11:22:14 | null | CloseReaderTest.java:22:4:22:15 | return ... | diff --git a/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.ql b/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.ql index 9de77b3c42b6..6fca436fbfdb 100644 --- a/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.ql +++ b/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.ql @@ -4,5 +4,5 @@ from ControlFlowNode n, ControlFlowNode succ where succ = n.getASuccessor() and n.getLocation().getFile().getExtension() = "java" and - not n.getFile().getStem() = "PopulateRuntimeException" + not n.getLocation().getFile().getStem() = "PopulateRuntimeException" select n, succ diff --git a/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.expected b/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.expected index 4598c7e0e32c..dcf2dac3cca1 100644 --- a/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.expected +++ b/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.expected @@ -1,4 +1,4 @@ -| LoopVarReadTest.java:3:14:3:28 | super(...) | LoopVarReadTest.java:3:14:3:28 | LoopVarReadTest | +| LoopVarReadTest.java:3:14:3:28 | super(...) | LoopVarReadTest.java:3:14:3:28 | Exit | | LoopVarReadTest.java:3:14:3:28 | { ... } | LoopVarReadTest.java:3:14:3:28 | super(...) | | LoopVarReadTest.java:5:2:15:2 | { ... } | LoopVarReadTest.java:6:3:6:12 | var ...; | | LoopVarReadTest.java:6:3:6:12 | var ...; | LoopVarReadTest.java:6:11:6:11 | 2 | @@ -23,6 +23,6 @@ | LoopVarReadTest.java:12:7:12:12 | q | LoopVarReadTest.java:14:3:14:28 | ; | | LoopVarReadTest.java:12:11:12:12 | 10 | LoopVarReadTest.java:12:7:12:12 | q | | LoopVarReadTest.java:14:3:14:12 | System.out | LoopVarReadTest.java:14:22:14:26 | "foo" | -| LoopVarReadTest.java:14:3:14:27 | println(...) | LoopVarReadTest.java:4:21:4:28 | testLoop | +| LoopVarReadTest.java:14:3:14:27 | println(...) | LoopVarReadTest.java:4:21:4:28 | Exit | | LoopVarReadTest.java:14:3:14:28 | ; | LoopVarReadTest.java:14:3:14:12 | System.out | | LoopVarReadTest.java:14:22:14:26 | "foo" | LoopVarReadTest.java:14:3:14:27 | println(...) | diff --git a/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.ql b/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.ql index 9de77b3c42b6..6fca436fbfdb 100644 --- a/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.ql +++ b/java/ql/test/library-tests/successors/LoopVarReadTest/TestSucc.ql @@ -4,5 +4,5 @@ from ControlFlowNode n, ControlFlowNode succ where succ = n.getASuccessor() and n.getLocation().getFile().getExtension() = "java" and - not n.getFile().getStem() = "PopulateRuntimeException" + not n.getLocation().getFile().getStem() = "PopulateRuntimeException" select n, succ diff --git a/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.expected b/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.expected index 2c6f433af5a4..3c261f67ee15 100644 --- a/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.expected +++ b/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.expected @@ -1,4 +1,4 @@ -| SaveFileTest.java:11:14:11:25 | super(...) | SaveFileTest.java:11:14:11:25 | SaveFileTest | +| SaveFileTest.java:11:14:11:25 | super(...) | SaveFileTest.java:11:14:11:25 | Exit | | SaveFileTest.java:11:14:11:25 | { ... } | SaveFileTest.java:11:14:11:25 | super(...) | | SaveFileTest.java:15:2:55:2 | { ... } | SaveFileTest.java:17:3:17:25 | var ...; | | SaveFileTest.java:17:3:17:25 | var ...; | SaveFileTest.java:17:21:17:24 | path | @@ -95,9 +95,9 @@ | SaveFileTest.java:48:5:48:15 | flush(...) | SaveFileTest.java:50:6:50:30 | catch (...) | | SaveFileTest.java:48:5:48:16 | ; | SaveFileTest.java:48:5:48:7 | bos | | SaveFileTest.java:49:5:49:7 | bos | SaveFileTest.java:49:5:49:15 | close(...) | -| SaveFileTest.java:49:5:49:15 | close(...) | SaveFileTest.java:12:14:12:21 | saveFile | +| SaveFileTest.java:49:5:49:15 | close(...) | SaveFileTest.java:12:14:12:21 | Exit | | SaveFileTest.java:49:5:49:15 | close(...) | SaveFileTest.java:50:6:50:30 | catch (...) | | SaveFileTest.java:49:5:49:16 | ; | SaveFileTest.java:49:5:49:7 | bos | | SaveFileTest.java:50:6:50:30 | catch (...) | SaveFileTest.java:50:23:50:29 | ignored | | SaveFileTest.java:50:23:50:29 | ignored | SaveFileTest.java:51:4:52:4 | { ... } | -| SaveFileTest.java:51:4:52:4 | { ... } | SaveFileTest.java:12:14:12:21 | saveFile | +| SaveFileTest.java:51:4:52:4 | { ... } | SaveFileTest.java:12:14:12:21 | Exit | diff --git a/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.ql b/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.ql index 9de77b3c42b6..6fca436fbfdb 100644 --- a/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.ql +++ b/java/ql/test/library-tests/successors/SaveFileTest/TestSucc.ql @@ -4,5 +4,5 @@ from ControlFlowNode n, ControlFlowNode succ where succ = n.getASuccessor() and n.getLocation().getFile().getExtension() = "java" and - not n.getFile().getStem() = "PopulateRuntimeException" + not n.getLocation().getFile().getStem() = "PopulateRuntimeException" select n, succ diff --git a/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected b/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected index 9bbb912e6d97..c645abe35072 100644 --- a/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected +++ b/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected @@ -1,8 +1,8 @@ -| SchackTest.java:1:14:1:23 | super(...) | SchackTest.java:1:14:1:23 | SchackTest | +| SchackTest.java:1:14:1:23 | super(...) | SchackTest.java:1:14:1:23 | Exit | | SchackTest.java:1:14:1:23 | { ... } | SchackTest.java:1:14:1:23 | super(...) | -| SchackTest.java:2:8:2:10 | super(...) | SchackTest.java:2:8:2:10 | ExA | +| SchackTest.java:2:8:2:10 | super(...) | SchackTest.java:2:8:2:10 | Exit | | SchackTest.java:2:8:2:10 | { ... } | SchackTest.java:2:8:2:10 | super(...) | -| SchackTest.java:3:8:3:10 | super(...) | SchackTest.java:3:8:3:10 | ExB | +| SchackTest.java:3:8:3:10 | super(...) | SchackTest.java:3:8:3:10 | Exit | | SchackTest.java:3:8:3:10 | { ... } | SchackTest.java:3:8:3:10 | super(...) | | SchackTest.java:5:18:24:2 | { ... } | SchackTest.java:6:3:23:3 | try ... | | SchackTest.java:6:3:23:3 | try ... | SchackTest.java:6:7:17:3 | { ... } | @@ -56,7 +56,7 @@ | SchackTest.java:20:23:20:72 | "successor (but neither true nor false successor)" | SchackTest.java:20:4:20:73 | println(...) | | SchackTest.java:21:13:23:3 | { ... } | SchackTest.java:22:4:22:41 | ; | | SchackTest.java:22:4:22:13 | System.out | SchackTest.java:22:23:22:39 | "false successor" | -| SchackTest.java:22:4:22:40 | println(...) | SchackTest.java:5:7:5:9 | foo | +| SchackTest.java:22:4:22:40 | println(...) | SchackTest.java:5:7:5:9 | Exit | | SchackTest.java:22:4:22:41 | ; | SchackTest.java:22:4:22:13 | System.out | | SchackTest.java:22:23:22:39 | "false successor" | SchackTest.java:22:4:22:40 | println(...) | | SchackTest.java:26:35:30:2 | { ... } | SchackTest.java:27:3:27:25 | if (...) | @@ -65,9 +65,9 @@ | SchackTest.java:27:7:27:24 | ... > ... | SchackTest.java:28:10:28:18 | new ExB(...) | | SchackTest.java:27:7:27:24 | ... > ... | SchackTest.java:29:10:29:22 | random(...) | | SchackTest.java:27:23:27:24 | .5 | SchackTest.java:27:7:27:24 | ... > ... | -| SchackTest.java:28:4:28:19 | throw ... | SchackTest.java:26:18:26:20 | bar | +| SchackTest.java:28:4:28:19 | throw ... | SchackTest.java:26:18:26:20 | Exit | | SchackTest.java:28:10:28:18 | new ExB(...) | SchackTest.java:28:4:28:19 | throw ... | -| SchackTest.java:29:3:29:28 | return ... | SchackTest.java:26:18:26:20 | bar | +| SchackTest.java:29:3:29:28 | return ... | SchackTest.java:26:18:26:20 | Exit | | SchackTest.java:29:10:29:22 | random(...) | SchackTest.java:29:26:29:27 | .3 | | SchackTest.java:29:10:29:27 | ... > ... | SchackTest.java:29:3:29:28 | return ... | | SchackTest.java:29:26:29:27 | .3 | SchackTest.java:29:10:29:27 | ... > ... | diff --git a/java/ql/test/library-tests/successors/SchackTest/TestSucc.ql b/java/ql/test/library-tests/successors/SchackTest/TestSucc.ql index 9de77b3c42b6..6fca436fbfdb 100644 --- a/java/ql/test/library-tests/successors/SchackTest/TestSucc.ql +++ b/java/ql/test/library-tests/successors/SchackTest/TestSucc.ql @@ -4,5 +4,5 @@ from ControlFlowNode n, ControlFlowNode succ where succ = n.getASuccessor() and n.getLocation().getFile().getExtension() = "java" and - not n.getFile().getStem() = "PopulateRuntimeException" + not n.getLocation().getFile().getStem() = "PopulateRuntimeException" select n, succ diff --git a/java/ql/test/library-tests/successors/TestBreak/TestSucc.expected b/java/ql/test/library-tests/successors/TestBreak/TestSucc.expected index e871d474a814..8dac71ffd45d 100644 --- a/java/ql/test/library-tests/successors/TestBreak/TestSucc.expected +++ b/java/ql/test/library-tests/successors/TestBreak/TestSucc.expected @@ -1,4 +1,4 @@ -| TestBreak.java:3:14:3:22 | super(...) | TestBreak.java:3:14:3:22 | TestBreak | +| TestBreak.java:3:14:3:22 | super(...) | TestBreak.java:3:14:3:22 | Exit | | TestBreak.java:3:14:3:22 | { ... } | TestBreak.java:3:14:3:22 | super(...) | | TestBreak.java:5:2:85:2 | { ... } | TestBreak.java:7:3:8:11 |