Skip to content

Commit d6338b9

Browse files
committed
Java: Count second level scopes for fieldFlowBranchLimit.
1 parent 600bc8c commit d6338b9

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ module JavaDataFlow implements InputSig<Location> {
2020

2121
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
2222

23+
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
24+
2325
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
2426

2527
predicate viableImplInCallContext = Private::viableImplInCallContext/2;

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll

+74-1
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,80 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
576576
/** Extra data-flow steps needed for lambda flow analysis. */
577577
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
578578

579-
class DataFlowSecondLevelScope = Unit;
579+
private predicate isTopLevel(Stmt s) {
580+
any(Callable c).getBody() = s
581+
or
582+
exists(BlockStmt b | s = b.getAStmt() and isTopLevel(b))
583+
}
584+
585+
private Stmt getAChainedBranch(IfStmt s) {
586+
result = s.getThen()
587+
or
588+
exists(Stmt elseBranch | s.getElse() = elseBranch |
589+
result = getAChainedBranch(elseBranch)
590+
or
591+
result = elseBranch and not elseBranch instanceof IfStmt
592+
)
593+
}
594+
595+
private newtype TDataFlowSecondLevelScope =
596+
TTopLevelIfBranch(Stmt s) {
597+
exists(IfStmt ifstmt | s = getAChainedBranch(ifstmt) and isTopLevel(ifstmt))
598+
} or
599+
TTopLevelSwitchCase(SwitchCase s) {
600+
exists(SwitchStmt switchstmt | s = switchstmt.getACase() and isTopLevel(switchstmt))
601+
}
602+
603+
private SwitchCase getPrecedingCase(Stmt s) {
604+
result = s
605+
or
606+
exists(SwitchStmt switch, int i |
607+
s = switch.getStmt(i) and
608+
not s instanceof SwitchCase and
609+
result = getPrecedingCase(switch.getStmt(i - 1))
610+
)
611+
}
612+
613+
/**
614+
* A second-level control-flow scope in a `switch` or a chained `if` statement.
615+
*
616+
* This is a `switch` case or a branch of a chained `if` statement, given that
617+
* the `switch` or `if` statement is top level, that is, it is not nested inside
618+
* other CFG constructs.
619+
*/
620+
class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
621+
/** Gets a textual representation of this element. */
622+
string toString() {
623+
exists(Stmt s | this = TTopLevelIfBranch(s) | result = s.toString())
624+
or
625+
exists(SwitchCase s | this = TTopLevelSwitchCase(s) | result = s.toString())
626+
}
627+
628+
/**
629+
* Gets a statement directly contained in this scope. For an `if` branch, this
630+
* is the branch itself, and for a `switch case`, this is one the statements
631+
* of that case branch.
632+
*/
633+
private Stmt getAStmt() {
634+
exists(Stmt s | this = TTopLevelIfBranch(s) | result = s)
635+
or
636+
exists(SwitchCase s | this = TTopLevelSwitchCase(s) |
637+
result = s.getRuleStatement() or
638+
s = getPrecedingCase(result)
639+
)
640+
}
641+
642+
/** Gets a data-flow node nested within this scope. */
643+
Node getANode() { getRelatedExpr(result).getAnEnclosingStmt() = this.getAStmt() }
644+
}
645+
646+
private Expr getRelatedExpr(Node n) {
647+
n.asExpr() = result or
648+
n.(PostUpdateNode).getPreUpdateNode().asExpr() = result
649+
}
650+
651+
/** Gets the second-level scope containing the node `n`, if any. */
652+
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
580653

581654
/**
582655
* Holds if flow is allowed to pass from parameter `p` and back to itself as a

0 commit comments

Comments
 (0)