Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions python/ql/lib/LegacyPointsTo.qll
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,11 @@ class ExprWithPointsTo extends Expr {
* Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
this.getAFlowNode()
.(ControlFlowNodeWithPointsTo)
.refersTo(context, obj, cls, origin.getAFlowNode())
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
)
}

/**
Expand All @@ -226,7 +228,11 @@ class ExprWithPointsTo extends Expr {
*/
pragma[nomagic]
predicate refersTo(Object obj, AstNode origin) {
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
)
}

/**
Expand All @@ -240,16 +246,22 @@ class ExprWithPointsTo extends Expr {
* in the given `context`.
*/
predicate pointsTo(Context context, Value value, AstNode origin) {
this.getAFlowNode()
.(ControlFlowNodeWithPointsTo)
.pointsTo(context, value, origin.getAFlowNode())
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
)
}

/**
* Holds if this expression might "point-to" to `value` which is from `origin`.
*/
predicate pointsTo(Value value, AstNode origin) {
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
)
}

/**
Expand Down Expand Up @@ -475,7 +487,10 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
not non_coupling_method(result) and
exists(Call call | call.getScope() = this |
exists(FunctionObject callee | callee.getFunction() = result |
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
exists(CallNode call_ |
call_.getNode() = call and
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
)
)
or
exists(Attribute a | call.getFunc() = a |
Expand Down
4 changes: 2 additions & 2 deletions python/ql/lib/analysis/DefinitionTracking.qll
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
private predicate preferred_jump_to_defn(Expr use, Definition def) {
not use instanceof ClassExpr and
not use instanceof FunctionExpr and
jump_to_defn(use.getAFlowNode(), def)
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
}

private predicate unique_jump_to_defn(Expr use, Definition def) {
Expand Down Expand Up @@ -452,7 +452,7 @@ private predicate self_parameter_jump_to_defn_attribute(
* This exists primarily for testing use `getPreferredDefinition()` instead.
*/
Definition getADefinition(Expr use) {
jump_to_defn(use.getAFlowNode(), result) and
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
not use instanceof Call and
not use.isArtificial() and
// Not the use itself
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: breaking
---
* The `AstNode.getAFlowNode()` predicate has been removed. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory refactor for migrating the dataflow library off the legacy CFG; it has no semantic effect.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged.
11 changes: 0 additions & 11 deletions python/ql/lib/semmle/python/AstExtended.qll
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,6 @@ abstract class AstNode extends AstNode_ {
/** Gets the scope that this node occurs in */
abstract Scope getScope();

/**
* Gets a flow node corresponding directly to this node.
* NOTE: For some statements and other purely syntactic elements,
* there may not be a `ControlFlowNode`
*/
cached
ControlFlowNode getAFlowNode() {
Stages::AST::ref() and
py_flow_bb_node(result, this, _, _)
}

/** Gets the location for this AST node */
cached
Location getLocation() { none() }
Expand Down
18 changes: 3 additions & 15 deletions python/ql/lib/semmle/python/Exprs.qll
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class Expr extends Expr_, AstNode {
/** Whether this expression may have a side effect (as determined purely from its syntax) */
predicate hasSideEffects() {
/* If an exception raised by this expression handled, count that as a side effect */
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
exists(ControlFlowNode n | n.getNode() = this |
n.getASuccessor().getNode() instanceof ExceptStmt
)
or
this.getASubExpression().hasSideEffects()
}
Expand Down Expand Up @@ -68,8 +70,6 @@ class Attribute extends Attribute_ {
/* syntax: Expr.name */
override Expr getASubExpression() { result = this.getObject() }

override AttrNode getAFlowNode() { result = super.getAFlowNode() }

/** Gets the name of this attribute. That is the `name` in `obj.name` */
string getName() { result = Attribute_.super.getAttr() }

Expand All @@ -96,8 +96,6 @@ class Subscript extends Subscript_ {
}

Expr getObject() { result = Subscript_.super.getValue() }

override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
}

/** A call expression, such as `func(...)` */
Expand All @@ -113,8 +111,6 @@ class Call extends Call_ {

override string toString() { result = this.getFunc().toString() + "()" }

override CallNode getAFlowNode() { result = super.getAFlowNode() }

/** Gets a tuple (*) argument of this call. */
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }

Expand Down Expand Up @@ -200,8 +196,6 @@ class IfExp extends IfExp_ {
override Expr getASubExpression() {
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
}

override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
}

/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
Expand Down Expand Up @@ -410,8 +404,6 @@ class PlaceHolder extends PlaceHolder_ {
override Expr getASubExpression() { none() }

override string toString() { result = "$" + this.getId() }

override NameNode getAFlowNode() { result = super.getAFlowNode() }
}

/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
Expand Down Expand Up @@ -478,8 +470,6 @@ class Name extends Name_ {

override string toString() { result = this.getId() }

override NameNode getAFlowNode() { result = super.getAFlowNode() }

override predicate isArtificial() {
/* Artificial variable names in comprehensions all start with "." */
this.getId().charAt(0) = "."
Expand Down Expand Up @@ -585,8 +575,6 @@ abstract class NameConstant extends Name, ImmutableLiteral {

override predicate isConstant() { any() }

override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }

override predicate isArtificial() { none() }
}

Expand Down
26 changes: 13 additions & 13 deletions python/ql/lib/semmle/python/Flow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
cached
DefinitionNode() {
Stages::AST::ref() and
exists(Assign a | a.getATarget().getAFlowNode() = this)
exists(Assign a | this.getNode() = a.getATarget())
or
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
exists(AssignExpr a | this.getNode() = a.getTarget())
or
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
exists(AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
or
exists(Alias a | a.getAsname().getAFlowNode() = this)
exists(Alias a | this.getNode() = a.getAsname())
or
augstore(_, this)
or
// `x, y = 1, 2` where LHS is a combination of list or tuples
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
exists(Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
or
exists(For for | for.getTarget().getAFlowNode() = this)
exists(For for | this.getNode() = for.getTarget())
or
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
exists(Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
}

/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
ControlFlowNode getValue() {
result = assigned_value(this.getNode()).getAFlowNode() and
result.getNode() = assigned_value(this.getNode()) and
(
result.getBasicBlock().dominates(this.getBasicBlock())
or
Expand All @@ -584,7 +584,7 @@ class DefinitionNode extends ControlFlowNode {
// since the default value for a parameter is evaluated in the same basic block as
// the function definition, but the parameter belongs to the basic block of the function,
// there is no dominance relationship between the two.
exists(Parameter param | this = param.asName().getAFlowNode())
exists(Parameter param | this.getNode() = param.asName())
)
}
}
Expand Down Expand Up @@ -901,7 +901,7 @@ class ExceptFlowNode extends ControlFlowNode {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result = ex.getType().getAFlowNode()
result.getNode() = ex.getType()
)
}

Expand All @@ -913,7 +913,7 @@ class ExceptFlowNode extends ControlFlowNode {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result = ex.getName().getAFlowNode()
result.getNode() = ex.getName()
)
}
}
Expand All @@ -928,7 +928,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
*/
ControlFlowNode getType() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
result.getNode() = this.getNode().(ExceptGroupStmt).getType()
}

/**
Expand All @@ -937,7 +937,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
*/
ControlFlowNode getName() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
result.getNode() = this.getNode().(ExceptGroupStmt).getName()
}
}

Expand Down
8 changes: 0 additions & 8 deletions python/ql/lib/semmle/python/Function.qll
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,6 @@ class Function extends Function_, Scope, AstNode {

override predicate contains(AstNode inner) { Scope.super.contains(inner) }

/** Gets a control flow node for a return value of this function */
ControlFlowNode getAReturnValueFlowNode() {
exists(Return ret |
ret.getScope() = this and
ret.getValue() = result.getNode()
)
}

/** Gets the minimum number of positional arguments that can be correctly passed to this function. */
int getMinPositionalArguments() {
result = count(this.getAnArg()) - count(this.getDefinition().getArgs().getADefault())
Expand Down
2 changes: 0 additions & 2 deletions python/ql/lib/semmle/python/Import.qll
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ class ImportMember extends ImportMember_ {
string getImportedModuleName() {
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
}

override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
}

/** An import statement */
Expand Down
17 changes: 10 additions & 7 deletions python/ql/lib/semmle/python/SelfAttribute.qll
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,23 @@ class SelfAttributeRead extends SelfAttribute {
}

predicate guardedByHasattr() {
exists(Variable var, ControlFlowNode n |
var.getAUse() = this.getObject().getAFlowNode() and
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
this_.getNode() = this and obj_.getNode() = this.getObject()
|
var.getAUse() = obj_ and
hasattr(n, var.getAUse(), this.getName()) and
n.strictlyDominates(this.getAFlowNode())
n.strictlyDominates(this_)
)
}

pragma[noinline]
predicate locallyDefined() {
exists(SelfAttributeStore store |
this.getName() = store.getName() and
this.getScope() = store.getScope()
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
store_.getNode() = store and this_.getNode() = this
|
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
this.getName() = store.getName() and
this.getScope() = store.getScope() and
store_.strictlyDominates(this_)
)
}
}
Expand Down
30 changes: 18 additions & 12 deletions python/ql/lib/semmle/python/dataflow/new/BarrierGuards.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,30 @@ private import semmle.python.dataflow.new.DataFlow

private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op |
op = any(Eq eq) and branch = true
or
op = any(NotEq ne) and branch = false
exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
c.getNode() = const and
(
op = any(Eq eq) and branch = true
or
op = any(NotEq ne) and branch = false
)
|
cn.operands(const.getAFlowNode(), op, node)
cn.operands(c, op, node)
or
cn.operands(node, op, const.getAFlowNode())
cn.operands(node, op, c)
)
or
exists(NameConstant const, Cmpop op |
op = any(Is is_) and branch = true
or
op = any(IsNot isn) and branch = false
exists(NameConstant const, Cmpop op, ControlFlowNode c |
c.getNode() = const and
(
op = any(Is is_) and branch = true
or
op = any(IsNot isn) and branch = false
)
|
cn.operands(const.getAFlowNode(), op, node)
cn.operands(c, op, node)
or
cn.operands(node, op, const.getAFlowNode())
cn.operands(node, op, c)
)
or
exists(IterableNode const_iterable, Cmpop op |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ private class ClassDefinitionAsAttrWrite extends AttrWrite, CfgNode {

override Node getValue() { result.asCfgNode() = node.getValue() }

override Node getObject() { result.asCfgNode() = cls.getAFlowNode() }
override Node getObject() { result.asCfgNode().getNode() = cls }

override ExprNode getAttributeNameExpr() { none() }

Expand Down
Loading
Loading