Make shared CFG construction library a parameterized module

This commit is contained in:
Tom Hvitved
2023-06-19 10:00:17 +02:00
parent 5049aafdd8
commit 1988397f93
17 changed files with 1629 additions and 2485 deletions

View File

@@ -486,7 +486,6 @@
], ],
"CFG": [ "CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll", "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
"swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll" "swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll"
], ],
"TypeTracker": [ "TypeTracker": [
@@ -573,4 +572,4 @@
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll", "python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll" "java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
] ]
} }

View File

@@ -1,8 +1,8 @@
import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::Consistency import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::Consistency
import codeql.ruby.AST import codeql.ruby.AST
import codeql.ruby.CFG import codeql.ruby.CFG
import codeql.ruby.controlflow.internal.Completion import codeql.ruby.controlflow.internal.Completion
import codeql.ruby.controlflow.internal.ControlFlowGraphImpl import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as CfgImpl
/** /**
* All `Expr` nodes are `PostOrderTree`s * All `Expr` nodes are `PostOrderTree`s
@@ -14,7 +14,7 @@ query predicate nonPostOrderExpr(Expr e, string cls) {
not e instanceof Namespace and not e instanceof Namespace and
not e instanceof Toplevel and not e instanceof Toplevel and
exists(AstNode last, Completion c | exists(AstNode last, Completion c |
last(e, last, c) and CfgImpl::last(e, last, c) and
last != e and last != e and
c instanceof NormalCompletion c instanceof NormalCompletion
) )

View File

@@ -3,7 +3,7 @@ private import codeql.ruby.CFG
private import internal.AST private import internal.AST
private import internal.TreeSitter private import internal.TreeSitter
private import internal.Variable private import internal.Variable
private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as CfgImpl
/** /**
* A statement. * A statement.
@@ -15,10 +15,10 @@ class Stmt extends AstNode, TStmt {
CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this }
/** Gets a control-flow entry node for this statement, if any */ /** Gets a control-flow entry node for this statement, if any */
AstNode getAControlFlowEntryNode() { result = getAControlFlowEntryNode(this) } AstNode getAControlFlowEntryNode() { result = CfgImpl::getAControlFlowEntryNode(this) }
/** Gets the control-flow scope of this statement, if any. */ /** Gets the control-flow scope of this statement, if any. */
CfgScope getCfgScope() { result = getCfgScope(this) } CfgScope getCfgScope() { result = CfgImpl::getCfgScope(this) }
/** Gets the enclosing callable, if any. */ /** Gets the enclosing callable, if any. */
Callable getEnclosingCallable() { result = this.getCfgScope() } Callable getEnclosingCallable() { result = this.getCfgScope() }

View File

@@ -4,7 +4,6 @@ private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.TreeSitter private import codeql.ruby.ast.internal.TreeSitter
private import codeql.ruby.controlflow.ControlFlowGraph private import codeql.ruby.controlflow.ControlFlowGraph
private import internal.ControlFlowGraphImpl
private import CfgNodes private import CfgNodes
private import SuccessorTypes private import SuccessorTypes

View File

@@ -6,16 +6,16 @@ private import codeql.ruby.dataflow.SSA
private import codeql.ruby.ast.internal.Constant private import codeql.ruby.ast.internal.Constant
private import codeql.ruby.ast.internal.Literal private import codeql.ruby.ast.internal.Literal
private import ControlFlowGraph private import ControlFlowGraph
private import internal.ControlFlowGraphImpl private import internal.ControlFlowGraphImpl as CfgImpl
private import internal.Splitting private import internal.Splitting
/** An entry node for a given scope. */ /** An entry node for a given scope. */
class EntryNode extends CfgNode, TEntryNode { class EntryNode extends CfgNode, CfgImpl::TEntryNode {
override string getAPrimaryQlClass() { result = "EntryNode" } override string getAPrimaryQlClass() { result = "EntryNode" }
private CfgScope scope; private CfgScope scope;
EntryNode() { this = TEntryNode(scope) } EntryNode() { this = CfgImpl::TEntryNode(scope) }
final override EntryBasicBlock getBasicBlock() { result = super.getBasicBlock() } final override EntryBasicBlock getBasicBlock() { result = super.getBasicBlock() }
@@ -25,13 +25,13 @@ class EntryNode extends CfgNode, TEntryNode {
} }
/** An exit node for a given scope, annotated with the type of exit. */ /** An exit node for a given scope, annotated with the type of exit. */
class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode { class AnnotatedExitNode extends CfgNode, CfgImpl::TAnnotatedExitNode {
override string getAPrimaryQlClass() { result = "AnnotatedExitNode" } override string getAPrimaryQlClass() { result = "AnnotatedExitNode" }
private CfgScope scope; private CfgScope scope;
private boolean normal; private boolean normal;
AnnotatedExitNode() { this = TAnnotatedExitNode(scope, normal) } AnnotatedExitNode() { this = CfgImpl::TAnnotatedExitNode(scope, normal) }
/** Holds if this node represent a normal exit. */ /** Holds if this node represent a normal exit. */
final predicate isNormal() { normal = true } final predicate isNormal() { normal = true }
@@ -52,12 +52,12 @@ class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode {
} }
/** An exit node for a given scope. */ /** An exit node for a given scope. */
class ExitNode extends CfgNode, TExitNode { class ExitNode extends CfgNode, CfgImpl::TExitNode {
override string getAPrimaryQlClass() { result = "ExitNode" } override string getAPrimaryQlClass() { result = "ExitNode" }
private CfgScope scope; private CfgScope scope;
ExitNode() { this = TExitNode(scope) } ExitNode() { this = CfgImpl::TExitNode(scope) }
final override Location getLocation() { result = scope.getLocation() } final override Location getLocation() { result = scope.getLocation() }
@@ -71,14 +71,14 @@ class ExitNode extends CfgNode, TExitNode {
* (dead) code or not important for control flow, and multiple when there are different * (dead) code or not important for control flow, and multiple when there are different
* splits for the AST node. * splits for the AST node.
*/ */
class AstCfgNode extends CfgNode, TElementNode { class AstCfgNode extends CfgNode, CfgImpl::TElementNode {
/** Gets the name of the primary QL class for this node. */ /** Gets the name of the primary QL class for this node. */
override string getAPrimaryQlClass() { result = "AstCfgNode" } override string getAPrimaryQlClass() { result = "AstCfgNode" }
private Splits splits; private CfgImpl::Splits splits;
AstNode e; AstNode e;
AstCfgNode() { this = TElementNode(_, e, splits) } AstCfgNode() { this = CfgImpl::TElementNode(_, e, splits) }
final override AstNode getNode() { result = e } final override AstNode getNode() { result = e }

View File

@@ -3,7 +3,7 @@
private import codeql.ruby.AST private import codeql.ruby.AST
private import codeql.ruby.controlflow.BasicBlocks private import codeql.ruby.controlflow.BasicBlocks
private import SuccessorTypes private import SuccessorTypes
private import internal.ControlFlowGraphImpl private import internal.ControlFlowGraphImpl as CfgImpl
private import internal.Splitting private import internal.Splitting
private import internal.Completion private import internal.Completion
@@ -15,12 +15,12 @@ private import internal.Completion
* Note that module declarations are not themselves CFG scopes, as they are part of * Note that module declarations are not themselves CFG scopes, as they are part of
* the CFG of the enclosing top-level or callable. * the CFG of the enclosing top-level or callable.
*/ */
class CfgScope extends Scope instanceof CfgScopeImpl { class CfgScope extends Scope instanceof CfgImpl::CfgScopeImpl {
/** Gets the CFG scope that this scope is nested under, if any. */ /** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() { final CfgScope getOuterCfgScope() {
exists(AstNode parent | exists(AstNode parent |
parent = this.getParent() and parent = this.getParent() and
result = getCfgScope(parent) result = CfgImpl::getCfgScope(parent)
) )
} }
} }
@@ -33,7 +33,7 @@ class CfgScope extends Scope instanceof CfgScopeImpl {
* *
* Only nodes that can be reached from an entry point are included in the CFG. * Only nodes that can be reached from an entry point are included in the CFG.
*/ */
class CfgNode extends TCfgNode { class CfgNode extends CfgImpl::TCfgNode {
/** Gets the name of the primary QL class for this node. */ /** Gets the name of the primary QL class for this node. */
string getAPrimaryQlClass() { none() } string getAPrimaryQlClass() { none() }
@@ -53,13 +53,13 @@ class CfgNode extends TCfgNode {
final predicate isCondition() { exists(this.getASuccessor(any(ConditionalSuccessor bs))) } final predicate isCondition() { exists(this.getASuccessor(any(ConditionalSuccessor bs))) }
/** Gets the scope of this node. */ /** Gets the scope of this node. */
final CfgScope getScope() { result = getNodeCfgScope(this) } final CfgScope getScope() { result = CfgImpl::getNodeCfgScope(this) }
/** Gets the basic block that this control flow node belongs to. */ /** Gets the basic block that this control flow node belongs to. */
BasicBlock getBasicBlock() { result.getANode() = this } BasicBlock getBasicBlock() { result.getANode() = this }
/** Gets a successor node of a given type, if any. */ /** Gets a successor node of a given type, if any. */
final CfgNode getASuccessor(SuccessorType t) { result = getASuccessor(this, t) } final CfgNode getASuccessor(SuccessorType t) { result = CfgImpl::getASuccessor(this, t) }
/** Gets an immediate successor, if any. */ /** Gets an immediate successor, if any. */
final CfgNode getASuccessor() { result = this.getASuccessor(_) } final CfgNode getASuccessor() { result = this.getASuccessor(_) }
@@ -78,7 +78,7 @@ class CfgNode extends TCfgNode {
} }
/** The type of a control flow successor. */ /** The type of a control flow successor. */
class SuccessorType extends TSuccessorType { class SuccessorType extends CfgImpl::TSuccessorType {
/** Gets a textual representation of successor type. */ /** Gets a textual representation of successor type. */
string toString() { none() } string toString() { none() }
} }
@@ -86,7 +86,7 @@ class SuccessorType extends TSuccessorType {
/** Provides different types of control flow successor types. */ /** Provides different types of control flow successor types. */
module SuccessorTypes { module SuccessorTypes {
/** A normal control flow successor. */ /** A normal control flow successor. */
class NormalSuccessor extends SuccessorType, TSuccessorSuccessor { class NormalSuccessor extends SuccessorType, CfgImpl::TSuccessorSuccessor {
final override string toString() { result = "successor" } final override string toString() { result = "successor" }
} }
@@ -99,9 +99,9 @@ module SuccessorTypes {
boolean value; boolean value;
ConditionalSuccessor() { ConditionalSuccessor() {
this = TBooleanSuccessor(value) or this = CfgImpl::TBooleanSuccessor(value) or
this = TEmptinessSuccessor(value) or this = CfgImpl::TEmptinessSuccessor(value) or
this = TMatchingSuccessor(value) this = CfgImpl::TMatchingSuccessor(value)
} }
/** Gets the Boolean value of this successor. */ /** Gets the Boolean value of this successor. */
@@ -125,7 +125,7 @@ module SuccessorTypes {
* *
* `x >= 0` has both a `true` successor and a `false` successor. * `x >= 0` has both a `true` successor and a `false` successor.
*/ */
class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor { } class BooleanSuccessor extends ConditionalSuccessor, CfgImpl::TBooleanSuccessor { }
/** /**
* An emptiness control flow successor. * An emptiness control flow successor.
@@ -158,7 +158,7 @@ module SuccessorTypes {
* \___/ * \___/
* ``` * ```
*/ */
class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor { class EmptinessSuccessor extends ConditionalSuccessor, CfgImpl::TEmptinessSuccessor {
override string toString() { if value = true then result = "empty" else result = "non-empty" } override string toString() { if value = true then result = "empty" else result = "non-empty" }
} }
@@ -189,7 +189,7 @@ module SuccessorTypes {
* puts "one" puts "not one" * puts "one" puts "not one"
* ``` * ```
*/ */
class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor { class MatchingSuccessor extends ConditionalSuccessor, CfgImpl::TMatchingSuccessor {
override string toString() { if value = true then result = "match" else result = "no-match" } override string toString() { if value = true then result = "match" else result = "no-match" }
} }
@@ -207,7 +207,7 @@ module SuccessorTypes {
* The exit node of `sum` is a `return` successor of the `return x + y` * The exit node of `sum` is a `return` successor of the `return x + y`
* statement. * statement.
*/ */
class ReturnSuccessor extends SuccessorType, TReturnSuccessor { class ReturnSuccessor extends SuccessorType, CfgImpl::TReturnSuccessor {
final override string toString() { result = "return" } final override string toString() { result = "return" }
} }
@@ -230,7 +230,7 @@ module SuccessorTypes {
* *
* The node `puts "done"` is `break` successor of the node `break`. * The node `puts "done"` is `break` successor of the node `break`.
*/ */
class BreakSuccessor extends SuccessorType, TBreakSuccessor { class BreakSuccessor extends SuccessorType, CfgImpl::TBreakSuccessor {
final override string toString() { result = "break" } final override string toString() { result = "break" }
} }
@@ -253,7 +253,7 @@ module SuccessorTypes {
* *
* The node `x >= 0` is `next` successor of the node `next`. * The node `x >= 0` is `next` successor of the node `next`.
*/ */
class NextSuccessor extends SuccessorType, TNextSuccessor { class NextSuccessor extends SuccessorType, CfgImpl::TNextSuccessor {
final override string toString() { result = "next" } final override string toString() { result = "next" }
} }
@@ -278,7 +278,7 @@ module SuccessorTypes {
* *
* The node `x -= 1` is `redo` successor of the node `redo`. * The node `x -= 1` is `redo` successor of the node `redo`.
*/ */
class RedoSuccessor extends SuccessorType, TRedoSuccessor { class RedoSuccessor extends SuccessorType, CfgImpl::TRedoSuccessor {
final override string toString() { result = "redo" } final override string toString() { result = "redo" }
} }
@@ -302,7 +302,7 @@ module SuccessorTypes {
* *
* The node `puts "Retry"` is `retry` successor of the node `retry`. * The node `puts "Retry"` is `retry` successor of the node `retry`.
*/ */
class RetrySuccessor extends SuccessorType, TRetrySuccessor { class RetrySuccessor extends SuccessorType, CfgImpl::TRetrySuccessor {
final override string toString() { result = "retry" } final override string toString() { result = "retry" }
} }
@@ -323,7 +323,7 @@ module SuccessorTypes {
* The exit node of `m` is an exceptional successor of the node * The exit node of `m` is an exceptional successor of the node
* `raise "x > 2"`. * `raise "x > 2"`.
*/ */
class RaiseSuccessor extends SuccessorType, TRaiseSuccessor { class RaiseSuccessor extends SuccessorType, CfgImpl::TRaiseSuccessor {
final override string toString() { result = "raise" } final override string toString() { result = "raise" }
} }
@@ -344,7 +344,7 @@ module SuccessorTypes {
* The exit node of `m` is an exit successor of the node * The exit node of `m` is an exit successor of the node
* `exit 1`. * `exit 1`.
*/ */
class ExitSuccessor extends SuccessorType, TExitSuccessor { class ExitSuccessor extends SuccessorType, CfgImpl::TExitSuccessor {
final override string toString() { result = "exit" } final override string toString() { result = "exit" }
} }
} }

View File

@@ -8,7 +8,7 @@ private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.Control private import codeql.ruby.ast.internal.Control
private import codeql.ruby.controlflow.ControlFlowGraph private import codeql.ruby.controlflow.ControlFlowGraph
private import ControlFlowGraphImpl private import ControlFlowGraphImpl as CfgImpl
private import NonReturning private import NonReturning
private import SuccessorTypes private import SuccessorTypes
@@ -53,7 +53,7 @@ private predicate nestedEnsureCompletion(TCompletion outer, int nestLevel) {
or or
outer = TExitCompletion() outer = TExitCompletion()
) and ) and
nestLevel = any(Trees::BodyStmtTree t).getNestLevel() nestLevel = any(CfgImpl::Trees::BodyStmtTree t).getNestLevel()
} }
pragma[noinline] pragma[noinline]
@@ -72,7 +72,7 @@ private predicate completionIsValidForStmt(AstNode n, Completion c) {
} }
private AstNode getARescuableBodyChild() { private AstNode getARescuableBodyChild() {
exists(Trees::BodyStmtTree bst | result = bst.getBodyChild(_, true) | exists(CfgImpl::Trees::BodyStmtTree bst | result = bst.getBodyChild(_, true) |
exists(bst.getARescue()) exists(bst.getARescue())
or or
exists(bst.getEnsure()) exists(bst.getEnsure())
@@ -247,7 +247,7 @@ private predicate inMatchingContext(AstNode n) {
or or
n = any(ReferencePattern p).getExpr() n = any(ReferencePattern p).getExpr()
or or
n.(Trees::DefaultValueParameterTree).hasDefaultValue() n.(CfgImpl::Trees::DefaultValueParameterTree).hasDefaultValue()
} }
/** /**

View File

@@ -1,74 +0,0 @@
private import codeql.ruby.AST as RB
private import ControlFlowGraphImpl as Impl
private import Completion as Comp
private import codeql.ruby.ast.internal.Synthesis
private import Splitting as Splitting
private import codeql.ruby.CFG as Cfg
/** The base class for `ControlFlowTree`. */
class ControlFlowTreeBase extends RB::AstNode {
ControlFlowTreeBase() { not any(Synthesis s).excludeFromControlFlowTree(this) }
}
class ControlFlowElement = RB::AstNode;
class Completion = Comp::Completion;
/**
* Hold if `c` represents normal evaluation of a statement or an
* expression.
*/
predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion }
/**
* Hold if `c` represents simple (normal) evaluation of a statement or an
* expression.
*/
predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion }
/** Holds if `c` is a valid completion for `e`. */
predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) }
class CfgScope = Cfg::CfgScope;
predicate getCfgScope = Impl::getCfgScope/1;
/** Holds if `first` is first executed when entering `scope`. */
predicate scopeFirst(CfgScope scope, ControlFlowElement first) {
scope.(Impl::CfgScopeImpl).entry(first)
}
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
predicate scopeLast(CfgScope scope, ControlFlowElement last, Completion c) {
scope.(Impl::CfgScopeImpl).exit(last, c)
}
/** The maximum number of splits allowed for a given node. */
int maxSplits() { result = 5 }
class SplitKindBase = Splitting::TSplitKind;
class Split = Splitting::Split;
class SuccessorType = Cfg::SuccessorType;
/** Gets a successor type that matches completion `c`. */
SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() }
/**
* Hold if `c` represents simple (normal) evaluation of a statement or an
* expression.
*/
predicate successorTypeIsSimple(SuccessorType t) {
t instanceof Cfg::SuccessorTypes::NormalSuccessor
}
/** Holds if `t` is an abnormal exit type out of a CFG scope. */
predicate isAbnormalExitType(SuccessorType t) {
t instanceof Cfg::SuccessorTypes::RaiseSuccessor or
t instanceof Cfg::SuccessorTypes::ExitSuccessor
}
class Location = RB::Location;
class Node = Cfg::CfgNode;

View File

@@ -2,7 +2,7 @@
* Provides classes and predicates relevant for splitting the control flow graph. * Provides classes and predicates relevant for splitting the control flow graph.
*/ */
private import codeql.ruby.AST private import codeql.ruby.AST as Ast
private import Completion private import Completion
private import ControlFlowGraphImpl private import ControlFlowGraphImpl
private import SuccessorTypes private import SuccessorTypes
@@ -64,31 +64,36 @@ private module ConditionalCompletionSplitting {
int getNextListOrder() { result = 1 } int getNextListOrder() { result = 1 }
private class ConditionalCompletionSplitImpl extends SplitImpl, ConditionalCompletionSplit { private class ConditionalCompletionSplitImpl extends SplitImpl instanceof ConditionalCompletionSplit
{
ConditionalCompletion completion;
ConditionalCompletionSplitImpl() { this = TConditionalCompletionSplit(completion) }
override ConditionalCompletionSplitKind getKind() { any() } override ConditionalCompletionSplitKind getKind() { any() }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
succ(pred, succ, c) and succ(pred, succ, c) and
last(succ, _, completion) and last(succ, _, completion) and
( (
last(succ.(NotExpr).getOperand(), pred, c) and last(succ.(Ast::NotExpr).getOperand(), pred, c) and
completion.(BooleanCompletion).getDual() = c completion.(BooleanCompletion).getDual() = c
or or
last(succ.(LogicalAndExpr).getAnOperand(), pred, c) and last(succ.(Ast::LogicalAndExpr).getAnOperand(), pred, c) and
completion = c completion = c
or or
last(succ.(LogicalOrExpr).getAnOperand(), pred, c) and last(succ.(Ast::LogicalOrExpr).getAnOperand(), pred, c) and
completion = c completion = c
or or
last(succ.(StmtSequence).getLastStmt(), pred, c) and last(succ.(Ast::StmtSequence).getLastStmt(), pred, c) and
completion = c completion = c
or or
last(succ.(ConditionalExpr).getBranch(_), pred, c) and last(succ.(Ast::ConditionalExpr).getBranch(_), pred, c) and
completion = c completion = c
) )
or or
succ(pred, succ, c) and succ(pred, succ, c) and
succ instanceof WhenClause and succ instanceof Ast::WhenClause and
completion = c completion = c
} }
@@ -144,7 +149,7 @@ module EnsureSplitting {
/** Holds if this node is the entry node in the `ensure` block it belongs to. */ /** Holds if this node is the entry node in the `ensure` block it belongs to. */
predicate isEntryNode() { first(block.getEnsure(), this) } predicate isEntryNode() { first(block.getEnsure(), this) }
BodyStmt getBlock() { result = block } Ast::BodyStmt getBlock() { result = block }
pragma[noinline] pragma[noinline]
predicate isEntered(AstNode pred, int nestLevel, Completion c) { predicate isEntered(AstNode pred, int nestLevel, Completion c) {
@@ -221,12 +226,12 @@ module EnsureSplitting {
override string toString() { result = "ensure (" + nestLevel + ")" } override string toString() { result = "ensure (" + nestLevel + ")" }
} }
private class EnsureSplitImpl extends SplitImpl, EnsureSplit { private class EnsureSplitImpl extends SplitImpl instanceof EnsureSplit {
override EnsureSplitKind getKind() { result.getNestLevel() = this.getNestLevel() } override EnsureSplitKind getKind() { result.getNestLevel() = super.getNestLevel() }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
succ.(EnsureNode).isEntered(pred, this.getNestLevel(), c) and succ.(EnsureNode).isEntered(pred, super.getNestLevel(), c) and
this.getType().isSplitForEntryCompletion(c) super.getType().isSplitForEntryCompletion(c)
} }
override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } override predicate hasEntryScope(CfgScope scope, AstNode first) { none() }
@@ -253,8 +258,8 @@ module EnsureSplitting {
*/ */
private predicate exit(AstNode pred, Completion c, boolean inherited) { private predicate exit(AstNode pred, Completion c, boolean inherited) {
exists(Trees::BodyStmtTree block, EnsureSplitType type | exists(Trees::BodyStmtTree block, EnsureSplitType type |
this.exit0(pred, block, this.getNestLevel(), c) and this.exit0(pred, block, super.getNestLevel(), c) and
type = this.getType() type = super.getType()
| |
if last(block.getEnsure(), pred, c) if last(block.getEnsure(), pred, c)
then then
@@ -302,9 +307,9 @@ module EnsureSplitting {
// split must be able to exit with a `return` completion. // split must be able to exit with a `return` completion.
this.appliesToPredecessor(pred) and this.appliesToPredecessor(pred) and
exists(EnsureSplitImpl outer | exists(EnsureSplitImpl outer |
outer.getNestLevel() = this.getNestLevel() - 1 and outer.(EnsureSplit).getNestLevel() = super.getNestLevel() - 1 and
outer.exit(pred, c, inherited) and outer.exit(pred, c, inherited) and
this.getType() instanceof NormalSuccessor and super.getType() instanceof NormalSuccessor and
inherited = true inherited = true
) )
} }
@@ -335,10 +340,10 @@ module EnsureSplitting {
if en.isEntryNode() and en.getBlock() != pred.(EnsureNode).getBlock() if en.isEntryNode() and en.getBlock() != pred.(EnsureNode).getBlock()
then then
// entering a nested `ensure` block // entering a nested `ensure` block
en.getNestLevel() > this.getNestLevel() en.getNestLevel() > super.getNestLevel()
else else
// staying in the same (possibly nested) `ensure` block as `pred` // staying in the same (possibly nested) `ensure` block as `pred`
en.getNestLevel() >= this.getNestLevel() en.getNestLevel() >= super.getNestLevel()
) )
} }
} }

View File

@@ -1,7 +1,7 @@
private import codeql.ssa.Ssa as SsaImplCommon private import codeql.ssa.Ssa as SsaImplCommon
private import codeql.ruby.AST private import codeql.ruby.AST
private import codeql.ruby.CFG as Cfg private import codeql.ruby.CFG as Cfg
private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared as ControlFlowGraphImplShared private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
private import codeql.ruby.dataflow.SSA private import codeql.ruby.dataflow.SSA
private import codeql.ruby.ast.Variable private import codeql.ruby.ast.Variable
private import Cfg::CfgNodes::ExprNodes private import Cfg::CfgNodes::ExprNodes
@@ -124,7 +124,7 @@ private predicate capturedExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, Local
private predicate namespaceSelfExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, SelfVariable v) { private predicate namespaceSelfExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, SelfVariable v) {
exists(Namespace ns, AstNode last | exists(Namespace ns, AstNode last |
v.getDeclaringScope() = ns and v.getDeclaringScope() = ns and
last = ControlFlowGraphImplShared::getAControlFlowExitNode(ns) and last = ControlFlowGraphImpl::getAControlFlowExitNode(ns) and
if last = ns if last = ns
then bb.getNode(i).getAPredecessor().getNode() = last then bb.getNode(i).getAPredecessor().getNode() = last
else bb.getNode(i).getNode() = last else bb.getNode(i).getNode() = last

View File

@@ -7,7 +7,7 @@
* @tags ide-contextual-queries/print-cfg * @tags ide-contextual-queries/print-cfg
*/ */
private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::TestOutput private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::TestOutput
private import codeql.IDEContextual private import codeql.IDEContextual
/** /**

View File

@@ -6,6 +6,7 @@ dbscheme: ruby.dbscheme
upgrades: upgrades upgrades: upgrades
library: true library: true
dependencies: dependencies:
codeql/controlflow: ${workspace}
codeql/dataflow: ${workspace} codeql/dataflow: ${workspace}
codeql/mad: ${workspace} codeql/mad: ${workspace}
codeql/regex: ${workspace} codeql/regex: ${workspace}

File diff suppressed because it is too large Load Diff

View File

@@ -2,4 +2,6 @@ name: codeql/controlflow
version: 0.0.1-dev version: 0.0.1-dev
groups: shared groups: shared
library: true library: true
dependencies:
codeql/util: ${workspace}
warnOnImplicitThis: true warnOnImplicitThis: true

View File

@@ -0,0 +1,36 @@
/** Provides classes for working with locations. */
/**
* A location as given by a file, a start line, a start column,
* an end line, and an end column.
*
* For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
signature class LocationSig {
/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine();
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn();
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine();
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn();
/** Gets a textual representation of this location. */
bindingset[this]
string toString();
/**
* Holds if this element is at the specified location.
* The location spans column `startColumn` of line `startLine` to
* column `endColumn` of line `endLine` in file `filepath`.
* For more information, see
* [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filePath, int startLine, int startColumn, int endLine, int endColumn
);
}