Merge pull request #1839 from markshannon/python-rationalize-library

Python: rationalize library a bit.
This commit is contained in:
Rebecca Valentine
2019-08-28 10:15:36 -07:00
committed by GitHub
8 changed files with 267 additions and 282 deletions

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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"
}
}

View File

@@ -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_ {
}

View File

@@ -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) */

View File

@@ -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_ {
}

View File

@@ -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_ {

View File

@@ -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() }
*/
}