mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge pull request #1839 from markshannon/python-rationalize-library
Python: rationalize library a bit.
This commit is contained in:
@@ -3,7 +3,6 @@ import semmle.python.Operations
|
||||
import semmle.python.Variables
|
||||
import semmle.python.AstGenerated
|
||||
import semmle.python.AstExtended
|
||||
import semmle.python.AST
|
||||
import semmle.python.Function
|
||||
import semmle.python.Module
|
||||
import semmle.python.Class
|
||||
@@ -12,7 +11,6 @@ import semmle.python.Stmts
|
||||
import semmle.python.Exprs
|
||||
import semmle.python.Keywords
|
||||
import semmle.python.Comprehensions
|
||||
import semmle.python.Lists
|
||||
import semmle.python.Flow
|
||||
import semmle.python.Metrics
|
||||
import semmle.python.Constants
|
||||
@@ -28,7 +26,6 @@ import semmle.python.types.Version
|
||||
import semmle.python.types.Descriptors
|
||||
import semmle.python.protocols
|
||||
import semmle.python.SSA
|
||||
import semmle.python.Assigns
|
||||
import semmle.python.SelfAttribute
|
||||
import semmle.python.types.Properties
|
||||
import semmle.python.xml.XML
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import python
|
||||
|
||||
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||
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` */
|
||||
ControlFlowNode getAFlowNode() {
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
Location getLocation() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Whether this syntactic element is artificial, that is it is generated
|
||||
* by the compiler and is not present in the source */
|
||||
predicate isArtificial() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt().
|
||||
*/
|
||||
abstract AstNode getAChildNode();
|
||||
|
||||
/** Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt() applied to the parent.
|
||||
*/
|
||||
AstNode getParentNode() {
|
||||
result.getAChildNode() = this
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically */
|
||||
predicate contains(AstNode inner) {
|
||||
this.getAChildNode+() = inner
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
|
||||
predicate containsInScope(AstNode inner) {
|
||||
this.contains(inner) and
|
||||
this.getScope() = inner.getScope() and
|
||||
not inner instanceof Scope
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* In order to handle data flow and other analyses efficiently the extractor transforms various statements which perform binding in assignments.
|
||||
* These classes provide a wrapper to provide a more 'natural' interface to the syntactic elements transformed to assignments.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
|
||||
/** An assignment statement */
|
||||
class AssignStmt extends Assign {
|
||||
|
||||
AssignStmt() {
|
||||
not this instanceof FunctionDef and not this instanceof ClassDef
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "AssignStmt"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,61 @@
|
||||
import python
|
||||
|
||||
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||
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` */
|
||||
ControlFlowNode getAFlowNode() {
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
Location getLocation() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Whether this syntactic element is artificial, that is it is generated
|
||||
* by the compiler and is not present in the source */
|
||||
predicate isArtificial() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt().
|
||||
*/
|
||||
abstract AstNode getAChildNode();
|
||||
|
||||
/** Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt() applied to the parent.
|
||||
*/
|
||||
AstNode getParentNode() {
|
||||
result.getAChildNode() = this
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically */
|
||||
predicate contains(AstNode inner) {
|
||||
this.getAChildNode+() = inner
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
|
||||
predicate containsInScope(AstNode inner) {
|
||||
this.contains(inner) and
|
||||
this.getScope() = inner.getScope() and
|
||||
not inner instanceof Scope
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Parents */
|
||||
|
||||
/** Internal implementation class */
|
||||
@@ -116,3 +172,59 @@ class StringPartList extends StringPartList_ {
|
||||
|
||||
}
|
||||
|
||||
/* **** Lists ***/
|
||||
|
||||
/** A parameter list */
|
||||
class ParameterList extends @py_parameter_list {
|
||||
|
||||
Function getParent() {
|
||||
py_parameter_lists(this, result)
|
||||
}
|
||||
|
||||
/** Gets a parameter */
|
||||
Parameter getAnItem() {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, _)
|
||||
}
|
||||
|
||||
/** Gets the nth parameter */
|
||||
Parameter getItem(int index) {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, index)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = "ParameterList"
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
|
||||
class ComprehensionList extends ComprehensionList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of expressions */
|
||||
class ExprList extends ExprList_ {
|
||||
|
||||
}
|
||||
|
||||
|
||||
library class DictItemList extends DictItemList_ {
|
||||
|
||||
}
|
||||
|
||||
library class DictItemListParent extends DictItemListParent_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of strings (the primitive type string not Bytes or Unicode) */
|
||||
class StringList extends StringList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of aliases in an import statement */
|
||||
class AliasList extends AliasList_ {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import python
|
||||
import semmle.python.flow.NameNode
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
|
||||
/* Note about matching parent and child nodes and CFG splitting:
|
||||
@@ -961,10 +960,149 @@ class RaiseStmtNode extends ControlFlowNode {
|
||||
|
||||
}
|
||||
|
||||
private
|
||||
predicate defined_by(NameNode def, Variable v) {
|
||||
def.defines(v) or
|
||||
exists(NameNode p | defined_by(p, v) and p.getASuccessor() = def and not p.defines(v))
|
||||
/** A control flow node corresponding to a (plain variable) name expression, such as `var`.
|
||||
* `None`, `True` and `False` are excluded.
|
||||
*/
|
||||
class NameNode extends ControlFlowNode {
|
||||
|
||||
NameNode() {
|
||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
||||
or
|
||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
}
|
||||
|
||||
/** Whether this flow node defines the variable `v`. */
|
||||
predicate defines(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.defines(v))
|
||||
and not this.isLoad()
|
||||
}
|
||||
|
||||
/** Whether this flow node deletes the variable `v`. */
|
||||
predicate deletes(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.deletes(v))
|
||||
}
|
||||
|
||||
/** Whether this flow node uses the variable `v`. */
|
||||
predicate uses(Variable v) {
|
||||
this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v))
|
||||
or
|
||||
exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load)
|
||||
or
|
||||
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
|
||||
}
|
||||
|
||||
string getId() {
|
||||
result = this.getNode().(Name).getId()
|
||||
or
|
||||
result = this.getNode().(PlaceHolder).getId()
|
||||
}
|
||||
|
||||
/** Whether this is a use of a local variable. */
|
||||
predicate isLocal() {
|
||||
Scopes::local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a non-local variable. */
|
||||
predicate isNonLocal() {
|
||||
Scopes::non_local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a global (including builtin) variable. */
|
||||
predicate isGlobal() {
|
||||
Scopes::use_of_global_variable(this, _, _)
|
||||
}
|
||||
|
||||
predicate isSelf() {
|
||||
exists(SsaVariable selfvar |
|
||||
selfvar.isSelf() and selfvar.getAUse() = this
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
|
||||
NameConstantNode() {
|
||||
exists(NameConstant n | py_flow_bb_node(this, n, _, _))
|
||||
}
|
||||
|
||||
override deprecated predicate defines(Variable v) { none() }
|
||||
|
||||
override deprecated predicate deletes(Variable v) { none() }
|
||||
|
||||
/* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
deprecated predicate uses(Variable v) { none() }
|
||||
*/
|
||||
}
|
||||
|
||||
private module Scopes {
|
||||
|
||||
private predicate fast_local(NameNode n) {
|
||||
exists(FastLocalVariable v |
|
||||
n.uses(v) and
|
||||
v.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
predicate local(NameNode n) {
|
||||
fast_local(n)
|
||||
or
|
||||
exists(SsaVariable var |
|
||||
var.getAUse() = n and
|
||||
n.getScope() instanceof Class and
|
||||
exists(var.getDefinition())
|
||||
)
|
||||
}
|
||||
|
||||
predicate non_local(NameNode n) {
|
||||
exists(FastLocalVariable flv |
|
||||
flv.getALoad() = n.getNode() and
|
||||
not flv.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
// magic is fine, but we get questionable join-ordering of it
|
||||
pragma [nomagic]
|
||||
predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
||||
n.isLoad() and
|
||||
not non_local(n)
|
||||
and
|
||||
not exists(SsaVariable var |
|
||||
var.getAUse() = n |
|
||||
var.getVariable() instanceof FastLocalVariable
|
||||
or
|
||||
n.getScope() instanceof Class and
|
||||
not maybe_undefined(var)
|
||||
)
|
||||
and name = n.getId()
|
||||
and scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_defined(SsaVariable var) {
|
||||
exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete()
|
||||
or
|
||||
exists(SsaVariable input |
|
||||
input = var.getAPhiInput() |
|
||||
maybe_defined(input)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
var.getDefinition().isDelete()
|
||||
or
|
||||
maybe_undefined(var.getAPhiInput())
|
||||
or
|
||||
exists(BasicBlock incoming |
|
||||
exists(var.getAPhiInput()) and
|
||||
incoming.getASuccessor() = var.getDefinition().getBasicBlock() and
|
||||
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A basic block (ignoring exceptional flow edges to scope exit) */
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import python
|
||||
|
||||
/** A parameter list */
|
||||
class ParameterList extends @py_parameter_list {
|
||||
|
||||
Function getParent() {
|
||||
py_parameter_lists(this, result)
|
||||
}
|
||||
|
||||
/** Gets a parameter */
|
||||
Parameter getAnItem() {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, _)
|
||||
}
|
||||
|
||||
/** Gets the nth parameter */
|
||||
Parameter getItem(int index) {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, index)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = "ParameterList"
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
|
||||
class ComprehensionList extends ComprehensionList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of expressions */
|
||||
class ExprList extends ExprList_ {
|
||||
|
||||
}
|
||||
|
||||
|
||||
library class DictItemList extends DictItemList_ {
|
||||
|
||||
}
|
||||
|
||||
library class DictItemListParent extends DictItemListParent_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of strings (the primitive type string not Bytes or Unicode) */
|
||||
class StringList extends StringList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of aliases in an import statement */
|
||||
class AliasList extends AliasList_ {
|
||||
|
||||
}
|
||||
|
||||
@@ -95,6 +95,18 @@ class Assign extends Assign_ {
|
||||
}
|
||||
}
|
||||
|
||||
/** An assignment statement */
|
||||
class AssignStmt extends Assign {
|
||||
|
||||
AssignStmt() {
|
||||
not this instanceof FunctionDef and not this instanceof ClassDef
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "AssignStmt"
|
||||
}
|
||||
}
|
||||
|
||||
/** An augmented assignment statement, such as `x += y` */
|
||||
class AugAssign extends AugAssign_ {
|
||||
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
import python
|
||||
private import semmle.python.pointsto.Base
|
||||
|
||||
/** A control flow node corresponding to a (plain variable) name expression, such as `var`.
|
||||
* `None`, `True` and `False` are excluded.
|
||||
*/
|
||||
class NameNode extends ControlFlowNode {
|
||||
|
||||
NameNode() {
|
||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
||||
or
|
||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
}
|
||||
|
||||
/** Whether this flow node defines the variable `v`. */
|
||||
predicate defines(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.defines(v))
|
||||
and not this.isLoad()
|
||||
}
|
||||
|
||||
/** Whether this flow node deletes the variable `v`. */
|
||||
predicate deletes(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.deletes(v))
|
||||
}
|
||||
|
||||
/** Whether this flow node uses the variable `v`. */
|
||||
predicate uses(Variable v) {
|
||||
this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v))
|
||||
or
|
||||
exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load)
|
||||
or
|
||||
use_of_global_variable(this, v.getScope(), v.getId())
|
||||
}
|
||||
|
||||
string getId() {
|
||||
result = this.getNode().(Name).getId()
|
||||
or
|
||||
result = this.getNode().(PlaceHolder).getId()
|
||||
}
|
||||
|
||||
/** Whether this is a use of a local variable. */
|
||||
predicate isLocal() {
|
||||
local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a non-local variable. */
|
||||
predicate isNonLocal() {
|
||||
non_local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a global (including builtin) variable. */
|
||||
predicate isGlobal() {
|
||||
use_of_global_variable(this, _, _)
|
||||
}
|
||||
|
||||
predicate isSelf() {
|
||||
exists(SsaVariable selfvar |
|
||||
selfvar.isSelf() and selfvar.getAUse() = this
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private predicate fast_local(NameNode n) {
|
||||
exists(FastLocalVariable v |
|
||||
n.uses(v) and
|
||||
v.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate local(NameNode n) {
|
||||
fast_local(n)
|
||||
or
|
||||
exists(SsaVariable var |
|
||||
var.getAUse() = n and
|
||||
n.getScope() instanceof Class and
|
||||
exists(var.getDefinition())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate non_local(NameNode n) {
|
||||
exists(FastLocalVariable flv |
|
||||
flv.getALoad() = n.getNode() and
|
||||
not flv.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
// magic is fine, but we get questionable join-ordering of it
|
||||
pragma [nomagic]
|
||||
private predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
||||
n.isLoad() and
|
||||
not non_local(n)
|
||||
and
|
||||
not exists(SsaVariable var |
|
||||
var.getAUse() = n |
|
||||
var.getVariable() instanceof FastLocalVariable
|
||||
or
|
||||
n.getScope() instanceof Class and
|
||||
not maybe_undefined(var)
|
||||
)
|
||||
and name = n.getId()
|
||||
and scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_defined(SsaVariable var) {
|
||||
exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete()
|
||||
or
|
||||
exists(SsaVariable input |
|
||||
input = var.getAPhiInput() |
|
||||
maybe_defined(input)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
var.getDefinition().isDelete()
|
||||
or
|
||||
maybe_undefined(var.getAPhiInput())
|
||||
or
|
||||
exists(BasicBlock incoming |
|
||||
exists(var.getAPhiInput()) and
|
||||
incoming.getASuccessor() = var.getDefinition().getBasicBlock() and
|
||||
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
|
||||
)
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
|
||||
NameConstantNode() {
|
||||
exists(NameConstant n | py_flow_bb_node(this, n, _, _))
|
||||
}
|
||||
|
||||
override deprecated predicate defines(Variable v) { none() }
|
||||
|
||||
override deprecated predicate deletes(Variable v) { none() }
|
||||
|
||||
/* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
deprecated predicate uses(Variable v) { none() }
|
||||
*/
|
||||
}
|
||||
Reference in New Issue
Block a user