Python: Autoformat semmle/python top-level.

This commit is contained in:
Taus Brock-Nannestad
2020-03-20 16:41:45 +01:00
parent f406a45ce0
commit 810e91ea00
23 changed files with 1984 additions and 4675 deletions

View File

@@ -2,55 +2,52 @@ import python
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
abstract class AstNode extends AstNode_ {
/* Special comment for documentation generation.
/*
* Special comment for documentation generation.
* All subclasses of `AstNode` that represent concrete syntax should have
* a comment of the form: */
/* syntax: ... */
* a comment of the form:
*/
/* syntax: ... */
/** Gets the scope that this node occurs in */
abstract Scope getScope();
/** Gets a flow node corresponding directly to this node.
/**
* 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, _, _)
}
* there may not be a `ControlFlowNode`
*/
ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) }
/** Gets the location for this AST node */
Location getLocation() {
none()
}
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()
}
/**
* 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().
*/
/**
* 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
}
/**
* 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
}
predicate contains(AstNode inner) { this.getAChildNode+() = inner }
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
predicate containsInScope(AstNode inner) {
@@ -58,133 +55,86 @@ abstract class AstNode extends AstNode_ {
this.getScope() = inner.getScope() and
not inner instanceof Scope
}
}
/* Parents */
/** Internal implementation class */
library class FunctionParent extends FunctionParent_ { }
/** Internal implementation class */
library class FunctionParent extends FunctionParent_ {
}
library class ArgumentsParent extends ArgumentsParent_ { }
/** Internal implementation class */
library class ArgumentsParent extends ArgumentsParent_ {
}
library class ExprListParent extends ExprListParent_ { }
/** Internal implementation class */
library class ExprListParent extends ExprListParent_ {
}
library class ExprContextParent extends ExprContextParent_ { }
/** Internal implementation class */
library class ExprContextParent extends ExprContextParent_ {
}
library class StmtListParent extends StmtListParent_ { }
/** Internal implementation class */
library class StmtListParent extends StmtListParent_ {
}
library class StrListParent extends StrListParent_ { }
/** Internal implementation class */
library class StrListParent extends StrListParent_ {
}
/** Internal implementation class */
library class ExprParent extends ExprParent_ {
}
library class ExprParent extends ExprParent_ { }
library class DictItem extends DictItem_, AstNode {
override string toString() {
result = DictItem_.super.toString()
}
override string toString() { result = DictItem_.super.toString() }
override AstNode getAChildNode() { none() }
override Scope getScope() { none() }
}
/** A comprehension part, the 'for a in seq' part of [ a * a for a in seq ] */
class Comprehension extends Comprehension_, AstNode {
/** Gets the scope of this comprehension */
override Scope getScope() {
/* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */
exists(ListComp l |
this = l.getAGenerator() |
result = l.getScope()
)
exists(ListComp l | this = l.getAGenerator() | result = l.getScope())
}
override string toString() {
result = "Comprehension"
}
override string toString() { result = "Comprehension" }
override Location getLocation() {
result = Comprehension_.super.getLocation()
}
override Location getLocation() { result = Comprehension_.super.getLocation() }
override AstNode getAChildNode() {
result = this.getASubExpression()
}
override AstNode getAChildNode() { result = this.getASubExpression() }
Expr getASubExpression() {
result = this.getIter() or
result = this.getAnIf() or
result = this.getTarget()
}
}
class BytesOrStr extends BytesOrStr_ {
class BytesOrStr extends BytesOrStr_ { }
}
/** Part of a string literal formed by implicit concatenation.
/**
* Part of a string literal formed by implicit concatenation.
* For example the string literal "abc" expressed in the source as `"a" "b" "c"`
* would be composed of three `StringPart`s.
*
*/
class StringPart extends StringPart_, AstNode {
override Scope getScope() {
exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope())
or
exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope())
}
override AstNode getAChildNode() {
none()
}
override AstNode getAChildNode() { none() }
override string toString() {
result = StringPart_.super.toString()
}
override Location getLocation() {
result = StringPart_.super.getLocation()
}
override string toString() { result = StringPart_.super.toString() }
override Location getLocation() { result = StringPart_.super.getLocation() }
}
class StringPartList extends StringPartList_ {
}
class StringPartList extends StringPartList_ { }
/* **** Lists ***/
/** A parameter list */
class ParameterList extends @py_parameter_list {
Function getParent() {
py_parameter_lists(this, result)
}
Function getParent() { py_parameter_lists(this, result) }
/** Gets a parameter */
Parameter getAnItem() {
@@ -198,40 +148,23 @@ class ParameterList extends @py_parameter_list {
py_exprs(result, _, this, index)
}
string toString() {
result = "ParameterList"
}
string toString() { result = "ParameterList" }
}
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
class ComprehensionList extends ComprehensionList_ {
}
class ComprehensionList extends ComprehensionList_ { }
/** A list of expressions */
class ExprList extends ExprList_ {
/* syntax: Expr, ... */
}
library class DictItemList extends DictItemList_ { }
library class DictItemList extends DictItemList_ {
}
library class DictItemListParent extends DictItemListParent_ {
}
library class DictItemListParent extends DictItemListParent_ { }
/** A list of strings (the primitive type string not Bytes or Unicode) */
class StringList extends StringList_ {
}
class StringList extends StringList_ { }
/** A list of aliases in an import statement */
class AliasList extends AliasList_ {
}
class AliasList extends AliasList_ { }

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,40 @@
import python
/** An (artificial) expression corresponding to a class definition.
/**
* An (artificial) expression corresponding to a class definition.
* It is recommended to use `ClassDef` instead.
*/
class ClassExpr extends ClassExpr_ {
/** Gets the metaclass expression */
Expr getMetaClass() {
if major_version() = 3 then
exists(Keyword metacls | this.getAKeyword() = metacls and metacls.getArg() = "metaclass" and result = metacls.getValue())
Expr getMetaClass() {
if major_version() = 3
then
exists(Keyword metacls |
this.getAKeyword() = metacls and
metacls.getArg() = "metaclass" and
result = metacls.getValue()
)
else
exists(Assign a | a = this.getInnerScope().getAStmt() and ((Name)a.getATarget()).getId() = "__metaclass__" and result = a.getValue())
exists(Assign a |
a = this.getInnerScope().getAStmt() and
a.getATarget().(Name).getId() = "__metaclass__" and
result = a.getValue()
)
}
/** Gets the nth keyword argument of this class definition. */
override DictUnpackingOrKeyword getKeyword(int index) {
result = this.getKeywords().getItem(index)
}
/** Gets a keyword argument of this class definition. */
override DictUnpackingOrKeyword getAKeyword() {
result = this.getKeywords().getAnItem()
}
override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() }
override Expr getASubExpression() {
result = this.getABase() or
result = this.getAKeyword().getValue() or
result = this.getKwargs() or
result = this.getStarargs()
result = this.getABase() or
result = this.getAKeyword().getValue() or
result = this.getKwargs() or
result = this.getStarargs()
}
Call getADecoratorCall() {
@@ -38,9 +43,7 @@ class ClassExpr extends ClassExpr_ {
}
/** Gets a decorator of this function expression */
Expr getADecorator() {
result = this.getADecoratorCall().getFunc()
}
Expr getADecorator() { result = this.getADecoratorCall().getFunc() }
override AstNode getAChildNode() {
result = this.getASubExpression()
@@ -49,151 +52,102 @@ class ClassExpr extends ClassExpr_ {
}
/** Gets a tuple (*) argument of this class definition. */
Expr getStarargs() {
result = this.getABase().(Starred).getValue()
}
Expr getStarargs() { result = this.getABase().(Starred).getValue() }
/** Gets a dictionary (**) argument of this class definition. */
Expr getKwargs() {
result = this.getAKeyword().(DictUnpacking).getValue()
}
Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() }
}
/** A class statement. Note that ClassDef extends Assign as a class definition binds the newly created class */
class ClassDef extends Assign {
/* syntax: class name(...): ... */
ClassDef() {
/* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */
exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall())
}
override string toString() {
result = "ClassDef"
}
override string toString() { result = "ClassDef" }
/** Gets the class for this statement */
Class getDefinedClass() {
exists(ClassExpr c |
this.getValue() = c or this.getValue() = c.getADecoratorCall() |
exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() |
result = c.getInnerScope()
)
}
override Stmt getLastStatement() {
result = this.getDefinedClass().getLastStatement()
}
override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() }
}
/** The scope of a class. This is the scope of all the statements within the class definition */
class Class extends Class_, Scope, AstNode {
/** Use getADecorator() instead of getDefinition().getADecorator()
/**
* Use getADecorator() instead of getDefinition().getADecorator()
* Use getMetaClass() instead of getDefinition().getMetaClass()
*/
deprecated ClassExpr getDefinition() {
result = this.getParent()
}
deprecated ClassExpr getDefinition() { result = this.getParent() }
/** Gets a defined init method of this class */
Function getInitMethod() {
result.getScope() = this and result.isInitMethod()
}
Function getInitMethod() { result.getScope() = this and result.isInitMethod() }
/** Gets a method defined in this class */
Function getAMethod() {
result.getScope() = this
}
Function getAMethod() { result.getScope() = this }
override Location getLocation() {
py_scope_location(result, this)
}
override Location getLocation() { py_scope_location(result, this) }
/** Gets the scope (module, class or function) in which this class is defined */
override Scope getEnclosingScope() {
result = this.getParent().getScope()
}
override Scope getEnclosingScope() { result = this.getParent().getScope() }
/** Use getEnclosingScope() instead */
override Scope getScope() {
result = this.getParent().getScope()
}
override Scope getScope() { result = this.getParent().getScope() }
override string toString() {
result = "Class " + this.getName()
}
override string toString() { result = "Class " + this.getName() }
/** Gets the statements forming the body of this class */
override StmtList getBody() {
result = Class_.super.getBody()
}
override StmtList getBody() { result = Class_.super.getBody() }
/** Gets the nth statement in the class */
override Stmt getStmt(int index) {
result = Class_.super.getStmt(index)
}
override Stmt getStmt(int index) { result = Class_.super.getStmt(index) }
/** Gets a statement in the class */
override Stmt getAStmt() {
result = Class_.super.getAStmt()
}
override Stmt getAStmt() { result = Class_.super.getAStmt() }
/** Gets the name used to define this class */
override string getName() {
result = Class_.super.getName()
}
override string getName() { result = Class_.super.getName() }
predicate hasSideEffects() {
any()
}
predicate hasSideEffects() { any() }
/** Whether this is probably a mixin (has 'mixin' or similar in name or docstring) */
predicate isProbableMixin() {
(this.getName().toLowerCase().matches("%mixin%")
or
this.getDocString().getText().toLowerCase().matches("%mixin%")
or
this.getDocString().getText().toLowerCase().matches("%mix-in%")
(
this.getName().toLowerCase().matches("%mixin%")
or
this.getDocString().getText().toLowerCase().matches("%mixin%")
or
this.getDocString().getText().toLowerCase().matches("%mix-in%")
)
}
override AstNode getAChildNode() {
result = this.getAStmt()
}
override AstNode getAChildNode() { result = this.getAStmt() }
Expr getADecorator() {
result = this.getParent().getADecorator()
}
Expr getADecorator() { result = this.getParent().getADecorator() }
/** Gets the metaclass expression */
Expr getMetaClass() {
result = this.getParent().getMetaClass()
}
Expr getMetaClass() { result = this.getParent().getMetaClass() }
/** Gets the ClassObject corresponding to this class */
ClassObject getClassObject() {
result.getOrigin() = this.getParent()
}
ClassObject getClassObject() { result.getOrigin() = this.getParent() }
/** Gets the nth base of this class definition. */
Expr getBase(int index) {
result = this.getParent().getBase(index)
}
Expr getBase(int index) { result = this.getParent().getBase(index) }
/** Gets a base of this class definition. */
Expr getABase() {
result = this.getParent().getABase()
}
Expr getABase() { result = this.getParent().getABase() }
/** Gets the metrics for this class */
ClassMetrics getMetrics() {
result = this
}
ClassMetrics getMetrics() { result = this }
/** Gets the qualified name for this class.
/**
* Gets the qualified name for this class.
* Should return the same name as the `__qualname__` attribute on classes in Python 3.
*/
string getQualifiedName() {
@@ -202,20 +156,13 @@ class Class extends Class_, Scope, AstNode {
exists(string enclosing_name |
enclosing_name = this.getScope().(Function).getQualifiedName()
or
enclosing_name = this.getScope().(Class).getQualifiedName() |
enclosing_name = this.getScope().(Class).getQualifiedName()
|
result = enclosing_name + "." + this.getName()
)
}
override
predicate containsInScope(AstNode inner) {
Scope.super.containsInScope(inner)
}
override
predicate contains(AstNode inner) {
Scope.super.contains(inner)
}
override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) }
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
}

View File

@@ -2,38 +2,28 @@ import python
/** A source code comment */
class Comment extends @py_comment {
/** Gets the full text of the comment including the leading '#' */
string getText() {
py_comments(this, result, _)
}
string getText() { py_comments(this, result, _) }
/** Gets the contents of the comment excluding the leading '#' */
string getContents() {
result = this.getText().suffix(1)
}
string getContents() { result = this.getText().suffix(1) }
Location getLocation() {
py_comments(this, _, result)
Location getLocation() { py_comments(this, _, result) }
}
string toString() { result = "Comment " + this.getText() }
string toString() {
result = "Comment " + this.getText()
}
/** Gets this immediately following comment.
/**
* Gets this immediately following comment.
* Blanks line are allowed between this comment and the following comment,
* but code or other comments are not.
*/
Comment getFollowing() {
exists(File f, int n |
this.file_line(f, n) |
result.file_line(f, n+1)
exists(File f, int n | this.file_line(f, n) |
result.file_line(f, n + 1)
or
result.file_line(f, n+2) and f.emptyLine(n+1)
result.file_line(f, n + 2) and f.emptyLine(n + 1)
or
result.file_line(f, n+3) and f.emptyLine(n+2) and f.emptyLine(n+1)
result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1)
)
}
@@ -41,47 +31,34 @@ class Comment extends @py_comment {
this.getLocation().getFile() = f and
this.getLocation().getStartLine() = n
}
}
private predicate comment_block_part(Comment start, Comment part, int i) {
not exists(Comment prev | prev.getFollowing() = part) and
exists(Comment following | part.getFollowing() = following) and
start = part and i = 1
start = part and
i = 1
or
exists(Comment prev |
comment_block_part(start, prev, i-1) and
comment_block_part(start, prev, i - 1) and
part = prev.getFollowing()
)
}
/** A block of consecutive comments */
class CommentBlock extends @py_comment {
CommentBlock() { comment_block_part(this, _, _) }
CommentBlock() {
comment_block_part(this, _, _)
}
private Comment last() { comment_block_part(this, result, this.length()) }
private Comment last() {
comment_block_part(this, result, this.length())
}
string toString() {
result = "Comment block"
}
string toString() { result = "Comment block" }
/** The length of this comment block (in comments) */
int length() {
result = max(int i | comment_block_part(this, _, i))
}
int length() { result = max(int i | comment_block_part(this, _, i)) }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
((Comment)this).getLocation().hasLocationInfo(filepath, bl, bc, _, _)
and
exists(Comment end |
end = this.last() |
end.getLocation().hasLocationInfo(_, _, _, el, ec)
)
this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and
exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, el, ec))
}
predicate contains(Comment c) {
@@ -91,20 +68,18 @@ class CommentBlock extends @py_comment {
}
string getContents() {
result = concat(Comment c,int i |
comment_block_part(this, c, i) or this = c and i = 0 |
c.getContents() order by i
)
result =
concat(Comment c, int i |
comment_block_part(this, c, i)
or
this = c and i = 0
|
c.getContents() order by i
)
}
}
/** A type-hint comment. Any comment that starts with `# type:` */
class TypeHintComment extends Comment {
TypeHintComment() {
this.getText().regexpMatch("# +type:.*")
}
TypeHintComment() { this.getText().regexpMatch("# +type:.*") }
}

View File

@@ -1,48 +1,64 @@
import python
/** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */
class CompareOp extends int {
CompareOp() {
this in [1..6]
}
CompareOp() { this in [1 .. 6] }
/** Gets the logical inverse operator */
CompareOp invert() {
this = eq() and result = ne() or
this = ne() and result = eq() or
this = lt() and result = ge() or
this = gt() and result = le() or
this = le() and result = gt() or
this = eq() and result = ne()
or
this = ne() and result = eq()
or
this = lt() and result = ge()
or
this = gt() and result = le()
or
this = le() and result = gt()
or
this = ge() and result = lt()
}
/** Gets the reverse operator (swapping the operands) */
/** Gets the reverse operator (swapping the operands) */
CompareOp reverse() {
this = eq() and result = eq() or
this = ne() and result = ne() or
this = lt() and result = gt() or
this = gt() and result = lt() or
this = le() and result = ge() or
this = eq() and result = eq()
or
this = ne() and result = ne()
or
this = lt() and result = gt()
or
this = gt() and result = lt()
or
this = le() and result = ge()
or
this = ge() and result = le()
}
string repr() {
this = eq() and result = "==" or
this = ne() and result = "!=" or
this = lt() and result = "<" or
this = gt() and result = ">" or
this = le() and result = "<=" or
this = eq() and result = "=="
or
this = ne() and result = "!="
or
this = lt() and result = "<"
or
this = gt() and result = ">"
or
this = le() and result = "<="
or
this = ge() and result = ">="
}
predicate forOp(Cmpop op) {
op instanceof Eq and this = eq() or
op instanceof NotEq and this = ne() or
op instanceof Lt and this = lt() or
op instanceof LtE and this = le() or
op instanceof Gt and this = gt() or
op instanceof Eq and this = eq()
or
op instanceof NotEq and this = ne()
or
op instanceof Lt and this = lt()
or
op instanceof LtE and this = le()
or
op instanceof Gt and this = gt()
or
op instanceof GtE and this = ge()
}
@@ -52,31 +68,37 @@ class CompareOp extends int {
or
result = this.invert() and isTrue = false
}
}
CompareOp eq() { result = 1 }
CompareOp ne() { result = 2 }
CompareOp lt() { result = 3 }
CompareOp le() { result = 4 }
CompareOp gt() { result = 5 }
CompareOp ge() { result = 6 }
/* Workaround precision limits in floating point numbers */
bindingset[x] private predicate ok_magnitude(float x) {
x > -9007199254740992.0 // -2**53
and
bindingset[x]
private predicate ok_magnitude(float x) {
x > -9007199254740992.0 and // -2**53
x < 9007199254740992.0 // 2**53
}
bindingset[x,y] private float add(float x, float y) {
bindingset[x, y]
private float add(float x, float y) {
ok_magnitude(x) and
ok_magnitude(y) and
ok_magnitude(result) and
result = x + y
}
bindingset[x,y] private float sub(float x, float y) {
bindingset[x, y]
private float sub(float x, float y) {
ok_magnitude(x) and
ok_magnitude(y) and
ok_magnitude(result) and
@@ -84,7 +106,9 @@ bindingset[x,y] private float sub(float x, float y) {
}
/** Normalise equality cmp into the form `left op right + k`. */
private predicate test(ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k) {
private predicate test(
ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k
) {
simple_test(cmp, left, op, right) and k = 0
or
add_test(cmp, left, op, right, k)
@@ -98,82 +122,106 @@ private predicate test(ControlFlowNode cmp, ControlFlowNode left, CompareOp op,
/** Various simple tests in left op right + k form. */
private predicate simple_test(CompareNode cmp, ControlFlowNode l, CompareOp cmpop, ControlFlowNode r) {
exists(Cmpop op |
cmp.operands(l, op, r) and cmpop.forOp(op)
)
exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op))
}
private predicate add_test_left(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
exists(BinaryExprNode lhs, float c, float x, Num n |
private predicate add_test_left(
CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
exists(BinaryExprNode lhs, float c, float x, Num n |
lhs.getNode().getOp() instanceof Add and
test(cmp, lhs, op, r, c) and x = n.getN().toFloat() and k = sub(c, x) |
test(cmp, lhs, op, r, c) and
x = n.getN().toFloat() and
k = sub(c, x)
|
l = lhs.getLeft() and n = lhs.getRight().getNode()
or
l = lhs.getRight() and n = lhs.getLeft().getNode()
)
}
private predicate add_test_right(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
private predicate add_test_right(
CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
exists(BinaryExprNode rhs, float c, float x, Num n |
rhs.getNode().getOp() instanceof Add and
test(cmp, l, op, rhs, c) and x = n.getN().toFloat() and k = add(c, x) |
test(cmp, l, op, rhs, c) and
x = n.getN().toFloat() and
k = add(c, x)
|
r = rhs.getLeft() and n = rhs.getRight().getNode()
or
r = rhs.getRight() and n = rhs.getLeft().getNode()
)
}
/* left + x op right + c => left op right + (c-x)
left op (right + x) + c => left op right + (c+x) */
private predicate add_test(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
/*
* left + x op right + c => left op right + (c-x)
* left op (right + x) + c => left op right + (c+x)
*/
private predicate add_test(
CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
add_test_left(cmp, l, op, r, k)
or
add_test_right(cmp, l, op ,r, k)
add_test_right(cmp, l, op, r, k)
}
private predicate subtract_test_left(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
exists(BinaryExprNode lhs, float c, float x, Num n |
private predicate subtract_test_left(
CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
exists(BinaryExprNode lhs, float c, float x, Num n |
lhs.getNode().getOp() instanceof Sub and
test(cmp, lhs, op, r, c) and
l = lhs.getLeft() and n = lhs.getRight().getNode() and
x = n.getN().toFloat() |
l = lhs.getLeft() and
n = lhs.getRight().getNode() and
x = n.getN().toFloat()
|
k = add(c, x)
)
}
private predicate subtract_test_right(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
private predicate subtract_test_right(
CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
exists(BinaryExprNode rhs, float c, float x, Num n |
rhs.getNode().getOp() instanceof Sub and
test(cmp, l, op, rhs, c) and
r = rhs.getRight() and n = rhs.getLeft().getNode() and
x = n.getN().toFloat() |
r = rhs.getRight() and
n = rhs.getLeft().getNode() and
x = n.getN().toFloat()
|
k = sub(c, x)
)
}
/* left - x op right + c => left op right + (c+x)
left op (right - x) + c => left op right + (c-x) */
private predicate subtract_test(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
/*
* left - x op right + c => left op right + (c+x)
* left op (right - x) + c => left op right + (c-x)
*/
private predicate subtract_test(
CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
subtract_test_left(cmp, l, op, r, k)
or
subtract_test_right(cmp, l, op, r, k)
}
private predicate not_test(UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
u.getNode().getOp() instanceof Not
and
private predicate not_test(
UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k
) {
u.getNode().getOp() instanceof Not and
test(u.getOperand(), l, op.invert(), r, k)
}
/** A comparison which can be simplified to the canonical form `x OP y + k` where `x` and `y` are `ControlFlowNode`s,
/**
* A comparison which can be simplified to the canonical form `x OP y + k` where `x` and `y` are `ControlFlowNode`s,
* `k` is a floating point constant and `OP` is one of `<=`, `>`, `==` or `!=`.
*/
class Comparison extends ControlFlowNode {
Comparison() {
test(this, _, _, _, _)
}
Comparison() { test(this, _, _, _, _) }
/** Whether this condition tests `l op r + k` */
predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) {
@@ -182,14 +230,14 @@ class Comparison extends ControlFlowNode {
/** Whether this condition tests `l op k` */
predicate tests(ControlFlowNode l, CompareOp op, float k) {
exists(ControlFlowNode r, float x, float c |
test(this, l, op, r, c) |
exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) |
x = r.getNode().(Num).getN().toFloat() and
k = add(c, x)
)
}
/* The following predicates determine whether this test, when its result is `thisIsTrue`,
/*
* The following predicates determine whether this test, when its result is `thisIsTrue`,
* is equivalent to the predicate `v OP k` or `v1 OP v2 + k`.
* For example, the test `x <= y` being false, is equivalent to the predicate `x > y`.
*/
@@ -242,7 +290,8 @@ class Comparison extends ControlFlowNode {
this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k)
}
/** Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`.
/**
* Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`.
* In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue`
* imply the predicate that is equivalent to the result of `that` being `thatIsTrue`.
* For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`.
@@ -315,7 +364,7 @@ class Comparison extends ControlFlowNode {
this.equivalentToGt(thisIsTrue, v, k1) and
that.equivalentToGtEq(thatIsTrue, v, k2) and
ge(k1, k2)
or
or
/* `v >= k1` => `v > k2` iff k1 > k2 */
this.equivalentToGtEq(thisIsTrue, v, k1) and
that.equivalentToGt(thatIsTrue, v, k2) and
@@ -402,7 +451,6 @@ class Comparison extends ControlFlowNode {
ge(k1, k2)
)
}
}
/* Work around differences in floating-point comparisons between Python and QL */
@@ -412,41 +460,27 @@ private predicate is_zero(float x) {
x = -0.0
}
bindingset[x,y] private predicate lt(float x, float y) {
if is_zero(x) then
y > 0
else
x < y
}
bindingset[x, y]
private predicate lt(float x, float y) { if is_zero(x) then y > 0 else x < y }
bindingset[x,y] private predicate eq(float x, float y) {
if is_zero(x) then
is_zero(y)
else
x = y
}
bindingset[x, y]
private predicate eq(float x, float y) { if is_zero(x) then is_zero(y) else x = y }
bindingset[x,y] private predicate gt(float x, float y) {
lt(y, x)
}
bindingset[x, y]
private predicate gt(float x, float y) { lt(y, x) }
bindingset[x,y] private predicate le(float x, float y) {
lt(x, y) or eq(x, y)
}
bindingset[x, y]
private predicate le(float x, float y) { lt(x, y) or eq(x, y) }
bindingset[x,y] private predicate ge(float x, float y) {
lt(y, x) or eq(x, y)
}
bindingset[x, y]
private predicate ge(float x, float y) { lt(y, x) or eq(x, y) }
/** A basic block which terminates in a condition, splitting the subsequent control flow,
/**
* A basic block which terminates in a condition, splitting the subsequent control flow,
* in which the condition is an instance of `Comparison`
*/
class ComparisonControlBlock extends ConditionBlock {
ComparisonControlBlock() {
this.getLastNode() instanceof Comparison
}
ComparisonControlBlock() { this.getLastNode() instanceof Comparison }
/** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */
predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) {
@@ -466,16 +500,13 @@ class ComparisonControlBlock extends ConditionBlock {
)
}
Comparison getTest() {
this.getLastNode() = result
}
Comparison getTest() { this.getLastNode() = result }
/** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */
predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) {
exists(boolean controlSense |
this.controls(b, controlSense) and
this.controls(b, controlSense) and
this.getTest().impliesThat(controlSense, that, thatIsTrue)
)
}
}

View File

@@ -1,8 +1,7 @@
import python
/** Base class for list, set and dictionary comprehensions, and generator expressions. */
abstract class Comp extends Expr {
abstract Function getFunction();
/** Gets the iteration variable for the nth innermost generator of this list comprehension */
@@ -13,35 +12,30 @@ abstract class Comp extends Expr {
private For getNthInnerLoop(int n) {
n = 0 and result = this.getFunction().getStmt(0)
or
result = this.getNthInnerLoop(n-1).getStmt(0)
result = this.getNthInnerLoop(n - 1).getStmt(0)
}
/** Gets the iteration variable for a generator of this list comprehension */
Variable getAnIterationVariable() {
result = this.getIterationVariable(_)
}
Variable getAnIterationVariable() { result = this.getIterationVariable(_) }
/** Gets the scope in which the body of this list comprehension evaluates. */
Scope getEvaluatingScope() {
result = this.getFunction()
}
Scope getEvaluatingScope() { result = this.getFunction() }
/** Gets the expression for elements of this comprehension. */
Expr getElt() {
exists(Yield yield, Stmt body |
result = yield.getValue() and
body = this.getNthInnerLoop(_).getAStmt() |
body = this.getNthInnerLoop(_).getAStmt()
|
yield = body.(ExprStmt).getValue()
or
yield = body.(If).getStmt(0).(ExprStmt).getValue()
)
}
}
/** A list comprehension, such as `[ chr(x) for x in range(ord('A'), ord('Z')+1) ]` */
class ListComp extends ListComp_, Comp {
override Expr getASubExpression() {
result = this.getAGenerator().getASubExpression() or
result = this.getElt() or
@@ -54,9 +48,7 @@ class ListComp extends ListComp_, Comp {
result = this.getFunction()
}
override predicate hasSideEffects() {
any()
}
override predicate hasSideEffects() { any() }
/** Gets the scope in which the body of this list comprehension evaluates. */
override Scope getEvaluatingScope() {
@@ -66,89 +58,53 @@ class ListComp extends ListComp_, Comp {
}
/** Gets the iteration variable for the nth innermost generator of this list comprehension */
override Variable getIterationVariable(int n) {
result = Comp.super.getIterationVariable(n)
}
override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) }
override Function getFunction() {
result = ListComp_.super.getFunction()
}
override Function getFunction() { result = ListComp_.super.getFunction() }
override string toString() {
result = ListComp_.super.toString()
}
override Expr getElt() {
result = Comp.super.getElt()
}
override string toString() { result = ListComp_.super.toString() }
override Expr getElt() { result = Comp.super.getElt() }
}
/** A set comprehension such as `{ v for v in "0123456789" }` */
class SetComp extends SetComp_, Comp {
override Expr getASubExpression() {
result = this.getIterable()
}
override Expr getASubExpression() { result = this.getIterable() }
override AstNode getAChildNode() {
result = this.getASubExpression() or
result = this.getFunction()
}
override predicate hasSideEffects() {
any()
}
override Function getFunction() {
result = SetComp_.super.getFunction()
}
override predicate hasSideEffects() { any() }
override Function getFunction() { result = SetComp_.super.getFunction() }
}
/** A dictionary comprehension, such as `{ k:v for k, v in enumerate("0123456789") }` */
class DictComp extends DictComp_, Comp {
override Expr getASubExpression() {
result = this.getIterable()
}
override Expr getASubExpression() { result = this.getIterable() }
override AstNode getAChildNode() {
result = this.getASubExpression() or
result = this.getFunction()
}
override predicate hasSideEffects() {
any()
}
override Function getFunction() {
result = DictComp_.super.getFunction()
}
override predicate hasSideEffects() { any() }
override Function getFunction() { result = DictComp_.super.getFunction() }
}
/** A generator expression, such as `(var for var in iterable)` */
class GeneratorExp extends GeneratorExp_, Comp {
override Expr getASubExpression() {
result = this.getIterable()
}
override Expr getASubExpression() { result = this.getIterable() }
override AstNode getAChildNode() {
result = this.getASubExpression() or
result = this.getFunction()
}
override predicate hasSideEffects() {
any()
}
override Function getFunction() {
result = GeneratorExp_.super.getFunction()
}
override predicate hasSideEffects() { any() }
override Function getFunction() { result = GeneratorExp_.super.getFunction() }
}

View File

@@ -8,30 +8,25 @@ int major_version() {
or
not explicit_major_version(_) and
/* If there is more than one version, prefer 2 for backwards compatibilty */
(
if py_flags_versioned("version.major", "2", "2") then
result = 2
else
result = 3
)
(if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3)
}
/** the Python minor version number */
int minor_version() {
exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) |
result = v.toInt())
result = v.toInt()
)
}
/** the Python micro version number */
int micro_version() {
exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) |
result = v.toInt())
result = v.toInt()
)
}
private predicate explicit_major_version(int v) {
exists(string version |
py_flags_versioned("language.version", version, _) |
exists(string version | py_flags_versioned("language.version", version, _) |
version.charAt(0) = "2" and v = 2
or
version.charAt(0) = "3" and v = 3

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,14 @@
import python
/** A file */
class File extends Container {
File() {
files(this, _, _, _, _)
}
File() { files(this, _, _, _, _) }
/** DEPRECATED: Use `getAbsolutePath` instead. */
deprecated override string getName() {
result = this.getAbsolutePath()
}
deprecated override string getName() { result = this.getAbsolutePath() }
/** DEPRECATED: Use `getAbsolutePath` instead. */
deprecated string getFullName() {
result = this.getAbsolutePath()
}
deprecated string getFullName() { result = this.getAbsolutePath() }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0
@@ -30,8 +22,7 @@ class File extends Container {
/** Gets a short name for this file (just the file name) */
string getShortName() {
exists(string simple, string ext | files(this, _, simple, ext, _) |
result = simple + ext)
exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext)
}
private int lastLine() {
@@ -40,27 +31,21 @@ class File extends Container {
/** Whether line n is empty (it contains neither code nor comment). */
predicate emptyLine(int n) {
n in [0..this.lastLine()]
and
n in [0 .. this.lastLine()] and
not occupied_line(this, n)
}
string getSpecifiedEncoding() {
exists(Comment c, Location l |
l = c.getLocation() and l.getFile() = this |
exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this |
l.getStartLine() < 3 and
result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1)
)
}
override string getAbsolutePath() {
files(this, result, _, _, _)
}
override string getAbsolutePath() { files(this, result, _, _, _) }
/** Gets the URL of this file. */
override string getURL() {
result = "file://" + this.getAbsolutePath() + ":0:0:0:0"
}
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
override Container getImportRoot(int n) {
/* File stem must be a legal Python identifier */
@@ -68,56 +53,40 @@ class File extends Container {
result = this.getParent().getImportRoot(n)
}
/** Gets the contents of this file as a string.
/**
* Gets the contents of this file as a string.
* This will only work for those non-python files that
* are specified to be extracted.
*/
string getContents() {
file_contents(this, result)
}
string getContents() { file_contents(this, result) }
}
private predicate occupied_line(File f, int n) {
exists(Location l |
l.getFile() = f |
exists(Location l | l.getFile() = f |
l.getStartLine() = n
or
exists(StrConst s | s.getLocation() = l |
n in [l.getStartLine() .. l.getEndLine()]
)
)
exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()])
)
}
/** A folder (directory) */
class Folder extends Container {
Folder() {
folders(this, _, _)
}
Folder() { folders(this, _, _) }
/** DEPRECATED: Use `getAbsolutePath` instead. */
deprecated override string getName() {
result = this.getAbsolutePath()
}
deprecated override string getName() { result = this.getAbsolutePath() }
/** DEPRECATED: Use `getBaseName` instead. */
deprecated string getSimple() {
folders(this, _, result)
}
deprecated string getSimple() { folders(this, _, result) }
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0
}
override string getAbsolutePath() {
folders(this, result, _)
}
override string getAbsolutePath() { folders(this, result, _) }
/** Gets the URL of this folder. */
override string getURL() {
result = "folder://" + this.getAbsolutePath()
}
override string getURL() { result = "folder://" + this.getAbsolutePath() }
override Container getImportRoot(int n) {
this.isImportRoot(n) and result = this
@@ -126,32 +95,24 @@ class Folder extends Container {
this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and
result = this.getParent().getImportRoot(n)
}
}
/**
/**
* A container is an abstract representation of a file system object that can
* hold elements of interest.
* hold elements of interest.
*/
abstract class Container extends @container {
Container getParent() {
containerparent(result, this)
}
Container getParent() { containerparent(result, this) }
/** Gets a child of this container */
deprecated Container getChild() {
containerparent(this, result)
}
deprecated Container getChild() { containerparent(this, result) }
/**
* Gets a textual representation of the path of this container.
*
* This is the absolute path of the container.
*/
string toString() {
result = this.getAbsolutePath()
}
string toString() { result = this.getAbsolutePath() }
/** Gets the name of this container */
abstract string getName();
@@ -165,8 +126,9 @@ abstract class Container extends @container {
* if the root folder is not a reflexive, transitive parent of this container.
*/
string getRelativePath() {
exists (string absPath, string pref |
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) |
exists(string absPath, string pref |
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
absPath = pref and result = ""
or
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
@@ -175,36 +137,28 @@ abstract class Container extends @container {
}
/** Whether this file or folder is part of the standard library */
predicate inStdlib() {
this.inStdlib(_, _)
}
predicate inStdlib() { this.inStdlib(_, _) }
/** Whether this file or folder is part of the standard library
/**
* Whether this file or folder is part of the standard library
* for version `major.minor`
*/
predicate inStdlib(int major, int minor) {
exists(Module m |
m.getPath() = this and
m.getPath() = this and
m.inStdLib(major, minor)
)
}
/* Standard cross-language API */
/** Gets a file or sub-folder in this container. */
Container getAChildContainer() {
containerparent(this, result)
}
Container getAChildContainer() { containerparent(this, result) }
/** Gets a file in this container. */
File getAFile() {
result = this.getAChildContainer()
}
File getAFile() { result = this.getAChildContainer() }
/** Gets a sub-folder in this container. */
Folder getAFolder() {
result = this.getAChildContainer()
}
Folder getAFolder() { result = this.getAChildContainer() }
/**
* Gets the absolute, canonical path of this container, using forward slashes
@@ -276,9 +230,7 @@ abstract class Container extends @container {
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
* </table>
*/
string getExtension() {
result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
}
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
/**
* Gets the stem of this container, that is, the prefix of its base name up to
@@ -297,9 +249,7 @@ abstract class Container extends @container {
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
* </table>
*/
string getStem() {
result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
}
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
File getFile(string baseName) {
result = this.getAFile() and
@@ -311,9 +261,7 @@ abstract class Container extends @container {
result.getBaseName() = baseName
}
Container getParentContainer() {
this = result.getAChildContainer()
}
Container getParentContainer() { this = result.getAChildContainer() }
Container getChildContainer(string baseName) {
result = this.getAChildContainer() and
@@ -328,32 +276,31 @@ abstract class Container extends @container {
abstract string getURL();
/** Holds if this folder is on the import path. */
predicate isImportRoot() {
this.isImportRoot(_)
}
predicate isImportRoot() { this.isImportRoot(_) }
/** Holds if this folder is on the import path, at index `n` in the list of
/**
* Holds if this folder is on the import path, at index `n` in the list of
* paths. The list of paths is composed of the paths passed to the extractor and
* `sys.path`. */
predicate isImportRoot(int n) {
this.getName() = import_path_element(n)
}
* `sys.path`.
*/
predicate isImportRoot(int n) { this.getName() = import_path_element(n) }
/** Holds if this folder is the root folder for the standard library. */
predicate isStdLibRoot(int major, int minor) {
major = major_version() and minor = minor_version() and
major = major_version() and
minor = minor_version() and
this.isStdLibRoot()
}
/** Holds if this folder is the root folder for the standard library. */
predicate isStdLibRoot() {
/* Look for a standard lib module and find its import path
/*
* Look for a standard lib module and find its import path
* We use `os` as it is the most likely to be imported and
* `tty` because it is small for testing.
*/
exists(Module m |
m.getName() = "os" or m.getName() = "tty"
|
exists(Module m | m.getName() = "os" or m.getName() = "tty" |
m.getFile().getImportRoot() = this
)
}
@@ -371,7 +318,6 @@ abstract class Container extends @container {
/** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */
abstract Container getImportRoot(int n);
}
private string import_path_element(int n) {
@@ -379,53 +325,46 @@ private string import_path_element(int n) {
path = get_path("extractor.path") and k = 0
or
path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep))
|
|
py_flags_versioned("os.pathsep", pathsep, _) and
result = path.splitAt(pathsep, n-k).replaceAll("\\", "/")
result = path.splitAt(pathsep, n - k).replaceAll("\\", "/")
)
}
private string get_path(string name) {
py_flags_versioned(name, result, _)
}
private string get_path(string name) { py_flags_versioned(name, result, _) }
class Location extends @location {
/** Gets the file for this location */
File getFile() {
result = this.getPath()
}
File getFile() { result = this.getPath() }
private Container getPath() {
locations_default(this, result, _, _, _, _)
or
exists(Module m | locations_ast(this, m, _, _, _, _) |
result = m.getPath()
)
exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath())
}
/** Gets the start line of this location */
int getStartLine() {
locations_default(this, _, result, _, _, _)
or locations_ast(this,_,result,_,_,_)
locations_default(this, _, result, _, _, _) or
locations_ast(this, _, result, _, _, _)
}
/** Gets the start column of this location */
int getStartColumn() {
locations_default(this, _, _, result, _, _)
or locations_ast(this, _, _, result, _, _)
locations_default(this, _, _, result, _, _) or
locations_ast(this, _, _, result, _, _)
}
/** Gets the end line of this location */
int getEndLine() {
locations_default(this, _, _, _, result, _)
or locations_ast(this, _, _, _, result, _)
locations_default(this, _, _, _, result, _) or
locations_ast(this, _, _, _, result, _)
}
/** Gets the end column of this location */
int getEndColumn() {
locations_default(this, _, _, _, _, result)
or locations_ast(this, _, _, _, _, result)
locations_default(this, _, _, _, _, result) or
locations_ast(this, _, _, _, _, result)
}
string toString() {
@@ -436,20 +375,20 @@ class Location extends @location {
exists(File f | f.getAbsolutePath() = filepath |
locations_default(this, f, bl, bc, el, ec)
or
exists(Module m | m.getFile() = f |
locations_ast(this, m, bl, bc, el, ec))
)
exists(Module m | m.getFile() = f | locations_ast(this, m, bl, bc, el, ec))
)
}
}
/** A non-empty line in the source code */
class Line extends @py_line {
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
exists(Module m | m.getFile().getAbsolutePath() = filepath and
el = bl and bc = 1 and
py_line_lengths(this, m, bl, ec))
exists(Module m |
m.getFile().getAbsolutePath() = filepath and
el = bl and
bc = 1 and
py_line_lengths(this, m, bl, ec)
)
}
string toString() {
@@ -459,58 +398,37 @@ class Line extends @py_line {
}
/** Gets the line number of this line */
int getLineNumber() {
py_line_lengths(this, _, result, _)
}
int getLineNumber() { py_line_lengths(this, _, result, _) }
/** Gets the length of this line */
int getLength() {
py_line_lengths(this, _, _, result)
}
int getLength() { py_line_lengths(this, _, _, result) }
/** Gets the file for this line */
Module getModule() {
py_line_lengths(this, result, _, _)
}
Module getModule() { py_line_lengths(this, result, _, _) }
}
/**
/**
* A syntax error. Note that if there is a syntax error in a module,
* much information about that module will be lost
* much information about that module will be lost
*/
class SyntaxError extends Location {
SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) }
SyntaxError() {
py_syntax_error_versioned(this, _, major_version().toString())
}
override string toString() {
result = "Syntax Error"
}
override string toString() { result = "Syntax Error" }
/** Gets the message corresponding to this syntax error */
string getMessage() {
py_syntax_error_versioned(this, result, major_version().toString())
}
string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) }
}
/**
/**
* An encoding error. Note that if there is an encoding error in a module,
* much information about that module will be lost
* much information about that module will be lost
*/
class EncodingError extends SyntaxError {
EncodingError() {
/* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */
this.getMessage().toLowerCase().matches("% decode %")
}
override string toString() {
result = "Encoding Error"
}
override string toString() { result = "Encoding Error" }
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,37 @@
import python
/**
/**
* A function, independent of defaults and binding.
* It is the syntactic entity that is compiled to a code object.
* It is the syntactic entity that is compiled to a code object.
*/
class Function extends Function_, Scope, AstNode {
/** The expression defining this function */
CallableExpr getDefinition() {
result = this.getParent()
}
CallableExpr getDefinition() { result = this.getParent() }
/** The scope in which this function occurs, will be a class for a method,
* another function for nested functions, generator expressions or comprehensions,
* or a module for a plain function. */
override Scope getEnclosingScope() {
result = this.getParent().(Expr).getScope()
}
/**
* The scope in which this function occurs, will be a class for a method,
* another function for nested functions, generator expressions or comprehensions,
* or a module for a plain function.
*/
override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() }
override Scope getScope() {
result = this.getEnclosingScope()
}
override Scope getScope() { result = this.getEnclosingScope() }
/** Whether this function is declared in a class */
predicate isMethod() {
exists(Class cls | this.getEnclosingScope() = cls)
}
predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) }
/** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */
predicate isSpecialMethod() {
this.isMethod() and
exists(string name | this.getName() = name |
name.matches("\\_\\_%\\_\\_") and
name != "__init__")
name != "__init__"
)
}
/**
/**
* Whether this function is a generator function,
* that is whether it contains a yield or yield-from expression
* that is whether it contains a yield or yield-from expression
*/
predicate isGenerator() {
exists(Yield y | y.getScope() = this)
@@ -46,88 +40,63 @@ class Function extends Function_, Scope, AstNode {
}
/** Whether this function is declared in a class and is named `__init__` */
predicate isInitMethod() {
this.isMethod() and this.getName() = "__init__"
}
predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" }
/** Gets a decorator of this function */
Expr getADecorator() {
result = ((FunctionExpr)this.getDefinition()).getADecorator()
}
Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() }
/** Gets the name of the nth argument (for simple arguments) */
string getArgName(int index) {
result = ((Name)this.getArg(index)).getId()
}
string getArgName(int index) { result = this.getArg(index).(Name).getId() }
Parameter getArgByName(string name) {
result = this.getAnArg() and
result.(Name).getId() = name
}
override Location getLocation() {
py_scope_location(result, this)
}
override Location getLocation() { py_scope_location(result, this) }
override string toString() {
result = "Function " + this.getName()
}
override string toString() { result = "Function " + this.getName() }
/** Gets the statements forming the body of this function */
override StmtList getBody() {
result = Function_.super.getBody()
}
override StmtList getBody() { result = Function_.super.getBody() }
/** Gets the nth statement in the function */
override Stmt getStmt(int index) {
result = Function_.super.getStmt(index)
}
override Stmt getStmt(int index) { result = Function_.super.getStmt(index) }
/** Gets a statement in the function */
override Stmt getAStmt() {
result = Function_.super.getAStmt()
}
override Stmt getAStmt() { result = Function_.super.getAStmt() }
/** Gets the name used to define this function */
override string getName() {
result = Function_.super.getName()
}
override string getName() { result = Function_.super.getName() }
/** Gets the metrics for this function */
FunctionMetrics getMetrics() {
result = this
}
FunctionMetrics getMetrics() { result = this }
/** Gets the FunctionObject corresponding to this function */
FunctionObject getFunctionObject() {
result.getOrigin() = this.getDefinition()
}
FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() }
/** Whether this function is a procedure, that is, it has no explicit return statement and always returns None.
* Note that generator and async functions are not procedures as they return generators and coroutines respectively. */
/**
* Whether this function is a procedure, that is, it has no explicit return statement and always returns None.
* Note that generator and async functions are not procedures as they return generators and coroutines respectively.
*/
predicate isProcedure() {
not exists(this.getReturnNode()) and exists(this.getFallthroughNode()) and not this.isGenerator() and not this.isAsync()
not exists(this.getReturnNode()) and
exists(this.getFallthroughNode()) and
not this.isGenerator() and
not this.isAsync()
}
/** Gets the number of positional parameters */
int getPositionalParameterCount() {
result = count(this.getAnArg())
}
int getPositionalParameterCount() { result = count(this.getAnArg()) }
/** Gets the number of keyword-only parameters */
int getKeywordOnlyParameterCount() {
result = count(this.getAKwonlyarg())
}
int getKeywordOnlyParameterCount() { result = count(this.getAKwonlyarg()) }
/** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */
predicate hasVarArg() {
exists(this.getVararg())
}
predicate hasVarArg() { exists(this.getVararg()) }
/** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */
predicate hasKwArg() {
exists(this.getKwarg())
}
predicate hasKwArg() { exists(this.getKwarg()) }
override AstNode getAChildNode() {
result = this.getAStmt() or
@@ -136,7 +105,8 @@ class Function extends Function_, Scope, AstNode {
result = this.getKwarg()
}
/** Gets the qualified name for this function.
/**
* Gets the qualified name for this function.
* Should return the same name as the `__qualname__` attribute on functions in Python 3.
*/
string getQualifiedName() {
@@ -145,38 +115,30 @@ class Function extends Function_, Scope, AstNode {
exists(string enclosing_name |
enclosing_name = this.getEnclosingScope().(Function).getQualifiedName()
or
enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() |
enclosing_name = this.getEnclosingScope().(Class).getQualifiedName()
|
result = enclosing_name + "." + this.getName()
)
}
/** Gets the nth keyword-only parameter of this function. */
Name getKeywordOnlyArg(int n) {
result = Function_.super.getKwonlyarg(n)
}
Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) }
/** Gets a keyword-only parameter of this function. */
Name getAKeywordOnlyArg() {
result = this.getKeywordOnlyArg(_)
}
Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) }
override Scope getEvaluatingScope() {
major_version() = 2 and exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope())
major_version() = 2 and
exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope())
or
not exists(Comp comp | comp.getFunction() = this) and result = this
or
or
major_version() = 3 and result = this
}
override
predicate containsInScope(AstNode inner) {
Scope.super.containsInScope(inner)
}
override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) }
override
predicate contains(AstNode inner) {
Scope.super.contains(inner)
}
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
/** Gets a control flow node for a return value of this function */
ControlFlowNode getAReturnValueFlowNode() {
@@ -185,48 +147,36 @@ class Function extends Function_, Scope, AstNode {
ret.getValue() = result.getNode()
)
}
}
/** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */
class FunctionDef extends Assign {
/* syntax: def name(...): ... */
FunctionDef() {
/* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */
exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall())
}
override string toString() {
result = "FunctionDef"
}
override string toString() { result = "FunctionDef" }
/** Gets the function for this statement */
Function getDefinedFunction() {
exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope())
}
override Stmt getLastStatement() {
result = this.getDefinedFunction().getLastStatement()
}
override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() }
}
class FastLocalsFunction extends Function {
/** A function that uses 'fast' locals, stored in the frame not in a dictionary. */
FastLocalsFunction () {
not exists(ImportStar i | i.getScope() = this)
and
FastLocalsFunction() {
not exists(ImportStar i | i.getScope() = this) and
not exists(Exec e | e.getScope() = this)
}
}
/** A parameter. Either a Tuple or a Name (always a Name for Python 3) */
class Parameter extends Parameter_ {
Parameter() {
/* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */
exists(ParameterList pl | py_exprs(this, _, pl, _))
@@ -245,36 +195,29 @@ class Parameter extends Parameter_ {
}
/** Gets this parameter if it is a Name (not a Tuple) */
Name asName() {
result = this
}
Name asName() { result = this }
/** Gets this parameter if it is a Tuple (not a Name) */
Tuple asTuple() {
result = this
}
Tuple asTuple() { result = this }
/** Gets the expression for the default value of this parameter */
Expr getDefault() {
exists(Function f, int n, int c, int d, Arguments args |
args = f.getDefinition().getArgs() |
exists(Function f, int n, int c, int d, Arguments args | args = f.getDefinition().getArgs() |
f.getArg(n) = this and
c = count(f.getAnArg()) and
d = count(args.getADefault()) and
result = args.getDefault(d-c+n)
result = args.getDefault(d - c + n)
)
}
/** Gets the annotation expression of this parameter */
Expr getAnnotation() {
exists(Function f, int n, Arguments args |
args = f.getDefinition().getArgs() |
exists(Function f, int n, Arguments args | args = f.getDefinition().getArgs() |
f.getArg(n) = this and
result = args.getAnnotation(n)
)
or
exists(Function f, Arguments args |
args = f.getDefinition().getArgs() |
exists(Function f, Arguments args | args = f.getDefinition().getArgs() |
f.getKwarg() = this and
result = args.getKwargannotation()
or
@@ -283,21 +226,13 @@ class Parameter extends Parameter_ {
)
}
Variable getVariable() {
result.getAnAccess() = this.asName()
}
Variable getVariable() { result.getAnAccess() = this.asName() }
/** Gets the position of this parameter */
int getPosition() {
exists(Function f |
f.getArg(result) = this
)
}
int getPosition() { exists(Function f | f.getArg(result) = this) }
/** Gets the name of this parameter */
string getName() {
result = this.asName().getId()
}
string getName() { result = this.asName().getId() }
/** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */
predicate isSelf() {
@@ -307,45 +242,39 @@ class Parameter extends Parameter_ {
)
}
/** Holds if this parameter is a 'varargs' parameter.
/**
* Holds if this parameter is a 'varargs' parameter.
* The `varargs` in `f(a, b, *varargs)`.
*/
predicate isVarargs() {
exists(Function func | func.getVararg() = this)
}
predicate isVarargs() { exists(Function func | func.getVararg() = this) }
/** Holds if this parameter is a 'kwargs' parameter.
/**
* Holds if this parameter is a 'kwargs' parameter.
* The `kwargs` in `f(a, b, **kwargs)`.
*/
predicate isKwargs() {
exists(Function func | func.getKwarg() = this)
}
predicate isKwargs() { exists(Function func | func.getKwarg() = this) }
}
/** An expression that generates a callable object, either a function expression or a lambda */
abstract class CallableExpr extends Expr {
/** Gets the parameters of this callable.
* This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. */
/**
* Gets the parameters of this callable.
* This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module.
*/
abstract Arguments getArgs();
/** Gets the function scope of this code expression. */
abstract Function getInnerScope();
}
/** An (artificial) expression corresponding to a function definition. */
class FunctionExpr extends FunctionExpr_, CallableExpr {
override Expr getASubExpression() {
result = this.getArgs().getASubExpression() or
result = this.getReturns()
}
override predicate hasSideEffects() {
any()
}
override predicate hasSideEffects() { any() }
Call getADecoratorCall() {
result.getArg(0) = this or
@@ -353,9 +282,7 @@ class FunctionExpr extends FunctionExpr_, CallableExpr {
}
/** Gets a decorator of this function expression */
Expr getADecorator() {
result = this.getADecoratorCall().getFunc()
}
Expr getADecorator() { result = this.getADecoratorCall().getFunc() }
override AstNode getAChildNode() {
result = this.getASubExpression()
@@ -363,47 +290,32 @@ class FunctionExpr extends FunctionExpr_, CallableExpr {
result = this.getInnerScope()
}
override Function getInnerScope() {
result = FunctionExpr_.super.getInnerScope()
}
override Arguments getArgs() {
result = FunctionExpr_.super.getArgs()
}
override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() }
override Arguments getArgs() { result = FunctionExpr_.super.getArgs() }
}
/** A lambda expression, such as lambda x:x*x */
/** A lambda expression, such as lambda x:x*x */
class Lambda extends Lambda_, CallableExpr {
/** Gets the expression to the right of the colon in this lambda expression */
Expr getExpression() {
exists(Return ret | ret = this.getInnerScope().getStmt(0) |
result = ret.getValue())
exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue())
}
override Expr getASubExpression() {
result = this.getArgs().getASubExpression()
}
override Expr getASubExpression() { result = this.getArgs().getASubExpression() }
override AstNode getAChildNode() {
result = this.getASubExpression() or
result = this.getInnerScope()
}
override Function getInnerScope() {
result = Lambda_.super.getInnerScope()
}
override Arguments getArgs() {
result = Lambda_.super.getArgs()
}
override Function getInnerScope() { result = Lambda_.super.getInnerScope() }
override Arguments getArgs() { result = Lambda_.super.getArgs() }
}
/** The arguments in a function definition */
class Arguments extends Arguments_ {
Expr getASubExpression() {
result = this.getAKwDefault() or
result = this.getAnAnnotation() or
@@ -412,5 +324,3 @@ class Arguments extends Arguments_ {
result = this.getADefault()
}
}

View File

@@ -2,9 +2,10 @@ import python
/** A basic block which terminates in a condition, splitting the subsequent control flow */
class ConditionBlock extends BasicBlock {
ConditionBlock() {
exists(ControlFlowNode succ | succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor())
exists(ControlFlowNode succ |
succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor()
)
}
/** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */
@@ -13,12 +14,12 @@ class ConditionBlock extends BasicBlock {
* For this block to control the block 'controlled' with 'testIsTrue' the following must be true:
* Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'.
* Execution must have passed through the 'testIsTrue' edge leaving 'this'.
*
*
* Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled',
* the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor()
* so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that
* so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that
* all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor().
*
*
* For example, in the following python snippet:
* <code>
* if x:
@@ -26,7 +27,7 @@ class ConditionBlock extends BasicBlock {
* false_successor
* uncontrolled
* </code>
* false_successor dominates uncontrolled, but not all of its predecessors are this (if x)
* false_successor dominates uncontrolled, but not all of its predecessors are this (if x)
* or dominated by itself. Whereas in the following code:
* <code>
* if x:
@@ -35,22 +36,21 @@ class ConditionBlock extends BasicBlock {
* false_successor
* uncontrolled
* </code>
* the block 'while controlled' is controlled because all of its predecessors are this (if x)
* the block 'while controlled' is controlled because all of its predecessors are this (if x)
* or (in the case of 'also_controlled') dominated by itself.
*
*
* The additional constraint on the predecessors of the test successor implies
* that `this` strictly dominates `controlled` so that isn't necessary to check
* directly.
*/
exists(BasicBlock succ |
exists(BasicBlock succ |
testIsTrue = true and succ = this.getATrueSuccessor()
or
testIsTrue = false and succ = this.getAFalseSuccessor()
|
succ.dominates(controlled) and
forall(BasicBlock pred | pred.getASuccessor() = succ |
pred = this or succ.dominates(pred)
)
|
succ.dominates(controlled) and
forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred))
)
}
@@ -58,11 +58,11 @@ class ConditionBlock extends BasicBlock {
predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) {
this.controls(pred, testIsTrue) and succ = pred.getASuccessor()
or
pred = this and (
pred = this and
(
testIsTrue = true and succ = this.getATrueSuccessor()
or
testIsTrue = false and succ = this.getAFalseSuccessor()
)
}
}

View File

@@ -1,14 +1,12 @@
import python
private import semmle.python.types.Builtins
/** An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial;
* `import x` is transformed into `import x as x` */
/**
* An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial;
* `import x` is transformed into `import x as x`
*/
class Alias extends Alias_ {
Location getLocation() {
result = this.getValue().getLocation()
}
Location getLocation() { result = this.getValue().getLocation() }
}
private predicate valid_module_name(string name) {
@@ -19,37 +17,34 @@ private predicate valid_module_name(string name) {
/** An artificial expression representing an import */
class ImportExpr extends ImportExpr_ {
private string basePackageName(int n) {
n = 1 and result = this.getEnclosingModule().getPackageName()
or
exists(string bpnm1 | bpnm1 = this.basePackageName(n-1) and
or
exists(string bpnm1 |
bpnm1 = this.basePackageName(n - 1) and
bpnm1.matches("%.%") and
result = bpnm1.regexpReplaceAll("\\.[^.]*$", "")
)
}
private predicate implicitRelativeImportsAllowed() {
// relative imports are no longer allowed in Python 3
// relative imports are no longer allowed in Python 3
major_version() < 3 and
// and can be explicitly turned off in later versions of Python 2
not getEnclosingModule().hasFromFuture("absolute_import")
}
/** The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports,
* and level > 0 for explicit relative imports. */
/**
* The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports,
* and level > 0 for explicit relative imports.
*/
override int getLevel() {
exists(int l | l = super.getLevel() |
l > 0 and result = l
or
/* The extractor may set level to 0 even though relative imports apply */
l = 0 and (
if this.implicitRelativeImportsAllowed() then
result = -1
else
result = 0
)
l = 0 and
(if this.implicitRelativeImportsAllowed() then result = -1 else result = 0)
)
}
@@ -64,49 +59,47 @@ class ImportExpr extends ImportExpr_ {
}
private string qualifiedTopName() {
if (this.getLevel() <= 0) then (
result = this.getTopName()
) else (
if this.getLevel() <= 0
then result = this.getTopName()
else (
result = basePackageName(this.getLevel()) and
valid_module_name(result)
)
}
/** Gets the name by which the lowest level module or package is imported.
* NOTE: This is the name that used to import the module,
* which may not be the name of the module. */
/**
* Gets the name by which the lowest level module or package is imported.
* NOTE: This is the name that used to import the module,
* which may not be the name of the module.
*/
string bottomModuleName() {
result = relativeTopName() + this.remainderOfName()
or
(
not exists(relativeTopName()) and
result = this.qualifiedTopName() + this.remainderOfName()
)
not exists(relativeTopName()) and
result = this.qualifiedTopName() + this.remainderOfName()
}
/** Gets the name of topmost module or package being imported */
string topModuleName() {
result = relativeTopName()
or
(
not exists(relativeTopName()) and
result = this.qualifiedTopName()
)
not exists(relativeTopName()) and
result = this.qualifiedTopName()
}
/** Gets the full name of the module resulting from evaluating this import.
/**
* Gets the full name of the module resulting from evaluating this import.
* NOTE: This is the name that used to import the module,
* which may not be the name of the module. */
* which may not be the name of the module.
*/
string getImportedModuleName() {
exists(string bottomName | bottomName = this.bottomModuleName() |
if this.isTop() then
result = topModuleName()
else
result = bottomName
if this.isTop() then result = topModuleName() else result = bottomName
)
}
/** Gets the names of the modules that may be imported by this import.
/**
* Gets the names of the modules that may be imported by this import.
* For example this predicate would return 'x' and 'x.y' for `import x.y`
*/
string getAnImportedModuleName() {
@@ -115,26 +108,24 @@ class ImportExpr extends ImportExpr_ {
result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "")
}
override Expr getASubExpression() {
none()
}
override Expr getASubExpression() { none() }
override predicate hasSideEffects() {
any()
}
override predicate hasSideEffects() { any() }
private string getTopName() {
result = this.getName().regexpReplaceAll("\\..*", "")
}
private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") }
private string remainderOfName() {
not exists(this.getName()) and result = "" or
this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") or
not exists(this.getName()) and result = ""
or
this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "")
or
this.getLevel() > 0 and result = "." + this.getName()
}
/** Whether this import is relative, that is not absolute.
* See https://www.python.org/dev/peps/pep-0328/ */
/**
* Whether this import is relative, that is not absolute.
* See https://www.python.org/dev/peps/pep-0328/
*/
predicate isRelative() {
/* Implicit */
exists(this.relativeTopName())
@@ -142,24 +133,22 @@ class ImportExpr extends ImportExpr_ {
/* Explicit */
this.getLevel() > 0
}
}
/** A `from ... import ...` expression */
class ImportMember extends ImportMember_ {
override Expr getASubExpression() {
result = this.getModule()
}
override Expr getASubExpression() { result = this.getModule() }
override predicate hasSideEffects() {
/* Strictly this only has side-effects if the module is a package */
any()
}
/** Gets the full name of the module resulting from evaluating this import.
* NOTE: This is the name that used to import the module,
* which may not be the name of the module. */
/**
* Gets the full name of the module resulting from evaluating this import.
* NOTE: This is the name that used to import the module,
* which may not be the name of the module.
*/
string getImportedModuleName() {
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
}
@@ -169,26 +158,22 @@ class ImportMember extends ImportMember_ {
/** An import statement */
class Import extends Import_ {
/* syntax: import modname */
private ImportExpr getAModuleExpr() {
result = this.getAName().getValue()
or
result = ((ImportMember)this.getAName().getValue()).getModule()
or
result = this.getAName().getValue().(ImportMember).getModule()
}
/** Use getAnImportedModuleName(),
/**
* Use getAnImportedModuleName(),
* possibly combined with ModuleObject.importedAs()
* Gets a module imported by this import statement */
deprecated Module getAModule() {
result.getName() = this.getAnImportedModuleName()
}
* Gets a module imported by this import statement
*/
deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() }
/** Whether this a `from ... import ...` statement */
predicate isFromImport() {
this.getAName().getValue() instanceof ImportMember
}
predicate isFromImport() { this.getAName().getValue() instanceof ImportMember }
override Expr getASubExpression() {
result = this.getAModuleExpr() or
@@ -196,73 +181,61 @@ class Import extends Import_ {
result = this.getAName().getValue()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
/** Gets the name of an imported module.
/**
* Gets the name of an imported module.
* For example, for the import statement `import bar` which
* is a relative import in package "foo", this would return
* "foo.bar".
* The import statment `from foo import bar` would return
* The import statment `from foo import bar` would return
* `foo` and `foo.bar`
* */
*/
string getAnImportedModuleName() {
result = this.getAModuleExpr().getAnImportedModuleName()
or
exists(ImportMember m, string modname |
m = this.getAName().getValue() and
modname = m.getModule().(ImportExpr).getImportedModuleName() |
modname = m.getModule().(ImportExpr).getImportedModuleName()
|
result = modname
or
result = modname + "." + m.getName()
)
}
}
/** An import * statement */
class ImportStar extends ImportStar_ {
/* syntax: from modname import * */
ImportExpr getModuleExpr() {
result = this.getModule()
or
result = ((ImportMember)this.getModule()).getModule()
result = this.getModule().(ImportMember).getModule()
}
override string toString() {
result = "from " + this.getModuleExpr().getName() + " import *"
}
override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" }
/** Use getAnImportedModuleName(),
/**
* Use getAnImportedModuleName(),
* possibly combined with ModuleObject.importedAs()
* Gets the module imported by this import * statement
* Gets the module imported by this import * statement
*/
deprecated Module getTheModule() {
result.getName() = this.getImportedModuleName()
}
deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() }
override Expr getASubExpression() {
result = this.getModule()
}
override Expr getASubExpression() { result = this.getModule() }
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
/** Gets the name of the imported module. */
string getImportedModuleName() {
result = this.getModuleExpr().getImportedModuleName()
}
string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() }
}
/** A statement that imports a module. This can be any statement that includes the `import` keyword,
* such as `import sys`, `from sys import version` or `from sys import *`. */
/**
* A statement that imports a module. This can be any statement that includes the `import` keyword,
* such as `import sys`, `from sys import version` or `from sys import *`.
*/
class ImportingStmt extends Stmt {
ImportingStmt() {
this instanceof Import
or
@@ -271,9 +244,8 @@ class ImportingStmt extends Stmt {
/** Gets the name of an imported module. */
string getAnImportedModuleName() {
result = this.(Import).getAnImportedModuleName()
result = this.(Import).getAnImportedModuleName()
or
result = this.(ImportStar).getImportedModuleName()
}
}

View File

@@ -1,105 +1,60 @@
import python
class KeyValuePair extends KeyValuePair_, DictDisplayItem {
/* syntax: Expr : Expr */
override Location getLocation() { result = KeyValuePair_.super.getLocation() }
override Location getLocation() {
result = KeyValuePair_.super.getLocation()
}
override string toString() {
result = KeyValuePair_.super.toString()
}
override string toString() { result = KeyValuePair_.super.toString() }
/** Gets the value of this dictionary unpacking. */
override Expr getValue() {
result = KeyValuePair_.super.getValue()
}
override Expr getValue() { result = KeyValuePair_.super.getValue() }
override Scope getScope() {
result = this.getValue().getScope()
}
override Scope getScope() { result = this.getValue().getScope() }
override AstNode getAChildNode() {
result = this.getKey()
or
result = this.getValue()
}
}
/** A double-starred expression in a call or dict literal. */
class DictUnpacking extends DictUnpacking_, DictUnpackingOrKeyword, DictDisplayItem {
override Location getLocation() { result = DictUnpacking_.super.getLocation() }
override Location getLocation() {
result = DictUnpacking_.super.getLocation()
}
override string toString() {
result = DictUnpacking_.super.toString()
}
override string toString() { result = DictUnpacking_.super.toString() }
/** Gets the value of this dictionary unpacking. */
override Expr getValue() {
result = DictUnpacking_.super.getValue()
}
override Expr getValue() { result = DictUnpacking_.super.getValue() }
override Scope getScope() {
result = this.getValue().getScope()
}
override AstNode getAChildNode() {
result = this.getValue()
}
override Scope getScope() { result = this.getValue().getScope() }
override AstNode getAChildNode() { result = this.getValue() }
}
abstract class DictUnpackingOrKeyword extends DictItem {
abstract Expr getValue();
override string toString() {
result = "DictUnpackingOrKeyword with missing toString"
}
override string toString() { result = "DictUnpackingOrKeyword with missing toString" }
}
abstract class DictDisplayItem extends DictItem {
abstract Expr getValue();
override string toString() {
result = "DictDisplayItem with missing toString"
}
override string toString() { result = "DictDisplayItem with missing toString" }
}
/** A keyword argument in a call. For example `arg=expr` in `foo(0, arg=expr)` */
class Keyword extends Keyword_, DictUnpackingOrKeyword {
class Keyword extends Keyword_, DictUnpackingOrKeyword {
/* syntax: name = Expr */
override Location getLocation() { result = Keyword_.super.getLocation() }
override Location getLocation() {
result = Keyword_.super.getLocation()
}
override string toString() {
result = Keyword_.super.toString()
}
override string toString() { result = Keyword_.super.toString() }
/** Gets the value of this keyword argument. */
override Expr getValue() {
result = Keyword_.super.getValue()
}
override Expr getValue() { result = Keyword_.super.getValue() }
override Scope getScope() {
result = this.getValue().getScope()
}
override AstNode getAChildNode() {
result = this.getValue()
}
override Scope getScope() { result = this.getValue().getScope() }
override AstNode getAChildNode() { result = this.getValue() }
}

View File

@@ -2,29 +2,23 @@ import python
/** The metrics for a function */
class FunctionMetrics extends Function {
/** Gets the total number of lines (including blank lines)
* from the definition to the end of the function */
int getNumberOfLines() {
py_alllines(this, result)
}
/**
* Gets the total number of lines (including blank lines)
* from the definition to the end of the function
*/
int getNumberOfLines() { py_alllines(this, result) }
/** Gets the number of lines of code in the function */
int getNumberOfLinesOfCode() {
py_codelines(this, result)
}
int getNumberOfLinesOfCode() { py_codelines(this, result) }
/** Gets the number of lines of comments in the function */
int getNumberOfLinesOfComments() {
py_commentlines(this, result)
}
int getNumberOfLinesOfComments() { py_commentlines(this, result) }
/** Gets the number of lines of docstring in the function */
int getNumberOfLinesOfDocStrings() {
py_docstringlines(this, result)
}
int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) }
/** Cyclomatic complexity:
/**
* Cyclomatic complexity:
* The number of linearly independent paths through the source code.
* Computed as E - N + 2P,
* where
@@ -34,14 +28,17 @@ class FunctionMetrics extends Function {
*/
int getCyclomaticComplexity() {
exists(int E, int N |
N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable())
and
E = count(BasicBlock b1, BasicBlock b2 |
b1 = this.getABasicBlock() and b1.likelyReachable() and
b2 = this.getABasicBlock() and b2.likelyReachable() and
b2 = b1.getASuccessor() and not b1.unlikelySuccessor(b2)
)
|
N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and
E =
count(BasicBlock b1, BasicBlock b2 |
b1 = this.getABasicBlock() and
b1.likelyReachable() and
b2 = this.getABasicBlock() and
b2.likelyReachable() and
b2 = b1.getASuccessor() and
not b1.unlikelySuccessor(b2)
)
|
result = E - N + 2
)
}
@@ -51,24 +48,23 @@ class FunctionMetrics extends Function {
or
exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor())
}
/** Dependency of Callables
/**
* Dependency of Callables
* One callable "this" depends on another callable "result"
* if "this" makes some call to a method that may end up being "result".
*/
*/
FunctionMetrics getADependency() {
result != this and
not non_coupling_method(result) and
exists(Call call |
call.getScope() = this |
exists(FunctionObject callee |
callee.getFunction() = result |
exists(Call call | call.getScope() = this |
exists(FunctionObject callee | callee.getFunction() = result |
call.getAFlowNode().getFunction().refersTo(callee)
)
or
exists(Attribute a |
call.getFunc() = a |
unique_root_method(result, a.getName()) or
exists(Attribute a | call.getFunc() = a |
unique_root_method(result, a.getName())
or
exists(Name n | a.getObject() = n and n.getId() = "self" |
result.getScope() = this.getScope() and
result.getName() = a.getName()
@@ -76,145 +72,122 @@ class FunctionMetrics extends Function {
)
)
}
/** Afferent Coupling
/**
* Afferent Coupling
* the number of callables that depend on this method.
* This is sometimes called the "fan-in" of a method.
*/
int getAfferentCoupling() {
result = count(FunctionMetrics m | m.getADependency() = this )
}
/** Efferent Coupling
int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) }
/**
* Efferent Coupling
* the number of methods that this method depends on
* This is sometimes called the "fan-out" of a method.
*/
int getEfferentCoupling() {
result = count(FunctionMetrics m | this.getADependency() = m)
}
int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) }
int getNumberOfParametersWithoutDefault() {
result = this.getPositionalParameterCount() -
count(((FunctionExpr)this.getDefinition()).getArgs().getADefault())
result =
this.getPositionalParameterCount() -
count(this.getDefinition().(FunctionExpr).getArgs().getADefault())
}
int getStatementNestingDepth() {
result = max(Stmt s | s.getScope() = this | getNestingDepth(s))
}
int getNumberOfCalls() {
result = count(Call c | c.getScope() = this)
}
int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) }
int getNumberOfCalls() { result = count(Call c | c.getScope() = this) }
}
/** The metrics for a class */
class ClassMetrics extends Class {
/** Gets the total number of lines (including blank lines)
* from the definition to the end of the class */
int getNumberOfLines() {
py_alllines(this, result)
}
/**
* Gets the total number of lines (including blank lines)
* from the definition to the end of the class
*/
int getNumberOfLines() { py_alllines(this, result) }
/** Gets the number of lines of code in the class */
int getNumberOfLinesOfCode() {
py_codelines(this, result)
}
int getNumberOfLinesOfCode() { py_codelines(this, result) }
/** Gets the number of lines of comments in the class */
int getNumberOfLinesOfComments() {
py_commentlines(this, result)
}
int getNumberOfLinesOfComments() { py_commentlines(this, result) }
/** Gets the number of lines of docstrings in the class */
int getNumberOfLinesOfDocStrings() {
py_docstringlines(this, result)
}
int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) }
private predicate dependsOn(Class other) {
other != this and
(
exists(FunctionMetrics f1, FunctionMetrics f2 |
f1.getADependency() = f2 |
exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 |
f1.getScope() = this and f2.getScope() = other
)
or
exists(Function f, Call c, ClassObject cls |
c.getScope() = f and f.getScope() = this |
exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this |
c.getFunc().refersTo(cls) and
cls.getPyClass() = other
)
)
}
/** The afferent coupling of a class is the number of classes that
* directly depend on it.
/**
* The afferent coupling of a class is the number of classes that
* directly depend on it.
*/
int getAfferentCoupling() {
result = count(ClassMetrics t | t.dependsOn(this))
}
/** The efferent coupling of a class is the number of classes that
int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) }
/**
* The efferent coupling of a class is the number of classes that
* it directly depends on.
*/
int getEfferentCoupling() {
result = count(ClassMetrics t | this.dependsOn(t))
}
int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) }
int getInheritanceDepth() {
exists(ClassObject cls |
cls.getPyClass() = this |
result = max(classInheritanceDepth(cls))
)
exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls)))
}
/* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */
/* The aim of this metric is to try and determine whether a class
* represents one abstraction (good) or multiple abstractions (bad).
* If a class represents multiple abstractions, it should be split
* up into multiple classes.
*
* In the Chidamber and Kemerer method, this is measured as follows:
* n1 = number of pairs of distinct methods in a class that do *not*
* have at least one commonly accessed field
* n2 = number of pairs of distinct methods in a class that do
* have at least one commonly accessed field
* lcom = ((n1 - n2)/2 max 0)
*
/*
* The aim of this metric is to try and determine whether a class
* represents one abstraction (good) or multiple abstractions (bad).
* If a class represents multiple abstractions, it should be split
* up into multiple classes.
*
* In the Chidamber and Kemerer method, this is measured as follows:
* n1 = number of pairs of distinct methods in a class that do *not*
* have at least one commonly accessed field
* n2 = number of pairs of distinct methods in a class that do
* have at least one commonly accessed field
* lcom = ((n1 - n2)/2 max 0)
*
* We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2.
*/
/** should function f be excluded from the cohesion computation? */
predicate ignoreLackOfCohesion(Function f) {
f.isInitMethod() or f.isSpecialMethod()
}
predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() }
private predicate methodPair(Function m1, Function m2) {
m1.getScope() = this and
m2.getScope() = this and
not this.ignoreLackOfCohesion(m1) and
not this.ignoreLackOfCohesion(m1) and
not this.ignoreLackOfCohesion(m2) and
m1 != m2
}
private predicate one_accesses_other(Function m1, Function m2) {
this.methodPair(m1, m2) and
(
exists(SelfAttributeRead sa |
sa.getName() = m1.getName() and
sa.getScope() = m2
)
or
exists(SelfAttributeRead sa |
sa.getName() = m2.getName() and
sa.getScope() = m1
)
exists(SelfAttributeRead sa |
sa.getName() = m1.getName() and
sa.getScope() = m2
)
or
exists(SelfAttributeRead sa |
sa.getName() = m2.getName() and
sa.getScope() = m1
)
)
}
/** do m1 and m2 access a common field or one calls the other? */
private predicate shareField(Function m1, Function m2) {
this.methodPair(m1, m2) and
@@ -222,142 +195,117 @@ class ClassMetrics extends Class {
exists(SelfAttributeRead sa |
sa.getName() = name and
sa.getScope() = m1
)
and
) and
exists(SelfAttributeRead sa |
sa.getName() = name and
sa.getScope() = m2
)
)
}
private int similarMethodPairs() {
result = count(Function m1, Function m2 |
this.methodPair(m1, m2) and
(this.shareField(m1, m2) or this.one_accesses_other(m1, m2))
) / 2
result =
count(Function m1, Function m2 |
this.methodPair(m1, m2) and
(this.shareField(m1, m2) or this.one_accesses_other(m1, m2))
) / 2
}
private int methodPairs() {
result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2
result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2
}
/** return Chidamber and Kemerer Lack of Cohesion */
int getLackOfCohesionCK() {
exists(int n |
n = this.methodPairs() - 2 * this.similarMethodPairs()
and
n = this.methodPairs() - 2 * this.similarMethodPairs() and
result = n.maximum(0)
)
}
private predicate similarMethodPairDag(Function m1, Function m2, int line) {
(this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and
line = m1.getLocation().getStartLine() and
line < m2.getLocation().getStartLine()
}
private predicate subgraph(Function m, int line) {
this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _)
or
exists(Function other | this.subgraph(other, line) |
this.similarMethodPairDag(other, m, _) or
this.similarMethodPairDag(m, other, _)
)
}
predicate unionSubgraph(Function m, int line) {
line = min(int l | this.subgraph(m, l))
}
)
}
/** return Hitz and Montazeri Lack of Cohesion */
int getLackOfCohesionHM() {
result = count(int line |
this.unionSubgraph(_, line)
)
}
private predicate similarMethodPairDag(Function m1, Function m2, int line) {
(this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and
line = m1.getLocation().getStartLine() and
line < m2.getLocation().getStartLine()
}
private predicate subgraph(Function m, int line) {
this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _)
or
exists(Function other | this.subgraph(other, line) |
this.similarMethodPairDag(other, m, _) or
this.similarMethodPairDag(m, other, _)
)
}
predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) }
/** return Hitz and Montazeri Lack of Cohesion */
int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) }
}
private int classInheritanceDepth(ClassObject cls) {
/* Prevent run-away recursion in case of circular inheritance */
not cls.getASuperType() = cls
and
not cls.getASuperType() = cls and
(
exists(ClassObject sup |
cls.getABaseType() = sup |
result = classInheritanceDepth(sup) + 1
)
or
not exists(cls.getABaseType()) and (
major_version() = 2 and result = 0
or
major_version() > 2 and result = 1
)
exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1)
or
not exists(cls.getABaseType()) and
(
major_version() = 2 and result = 0
or
major_version() > 2 and result = 1
)
)
}
class ModuleMetrics extends Module {
/** Gets the total number of lines (including blank lines) in the module */
int getNumberOfLines() {
py_alllines(this, result)
}
int getNumberOfLines() { py_alllines(this, result) }
/** Gets the number of lines of code in the module */
int getNumberOfLinesOfCode() {
py_codelines(this, result)
}
/** Gets the number of lines of code in the module */
int getNumberOfLinesOfCode() { py_codelines(this, result) }
/** Gets the number of lines of comments in the module */
int getNumberOfLinesOfComments() {
py_commentlines(this, result)
}
int getNumberOfLinesOfComments() { py_commentlines(this, result) }
/** Gets the number of lines of docstrings in the module */
int getNumberOfLinesOfDocStrings() {
py_docstringlines(this, result)
}
/** The afferent coupling of a class is the number of classes that
* directly depend on it.
*/
int getAfferentCoupling() {
result = count(ModuleMetrics t | t.dependsOn(this))
}
int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) }
/** The efferent coupling of a class is the number of classes that
/**
* The afferent coupling of a class is the number of classes that
* directly depend on it.
*/
int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) }
/**
* The efferent coupling of a class is the number of classes that
* it directly depends on.
*/
int getEfferentCoupling() {
result = count(ModuleMetrics t | this.dependsOn(t))
}
int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) }
private predicate dependsOn(Module other) {
other != this and
(
exists(FunctionMetrics f1, FunctionMetrics f2 |
f1.getADependency() = f2 |
exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 |
f1.getEnclosingModule() = this and f2.getEnclosingModule() = other
)
or
exists(Function f, Call c, ClassObject cls |
c.getScope() = f and f.getScope() = this |
exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this |
c.getFunc().refersTo(cls) and
cls.getPyClass().getEnclosingModule() = other
)
)
}
}
/** Helpers for coupling */
predicate unique_root_method(Function func, string name) {
name = func.getName() and
not exists(FunctionObject f, FunctionObject other |
name = func.getName() and
not exists(FunctionObject f, FunctionObject other |
f.getFunction() = func and
other.getName() = name |
other.getName() = name
|
not other.overrides(f)
)
}
@@ -375,13 +323,11 @@ predicate non_coupling_method(Function f) {
private int getNestingDepth(Stmt s) {
not exists(Stmt outer | outer.getASubStatement() = s) and result = 1
or
exists(Stmt outer |
outer.getASubStatement() = s |
if s.(If).isElif() or s instanceof ExceptStmt then
exists(Stmt outer | outer.getASubStatement() = s |
if s.(If).isElif() or s instanceof ExceptStmt
then
/* If statement is an `elif` or `except` then it is not indented relative to its parent */
result = getNestingDepth(outer)
else
result = getNestingDepth(outer) + 1
else result = getNestingDepth(outer) + 1
)
}

View File

@@ -2,47 +2,42 @@ import python
private import semmle.python.objects.ObjectAPI
private import semmle.python.objects.Modules
/** A module. This is the top level element in an AST, corresponding to a source file.
* It is also a Scope; the scope of global variables. */
/**
* A module. This is the top level element in an AST, corresponding to a source file.
* It is also a Scope; the scope of global variables.
*/
class Module extends Module_, Scope, AstNode {
override string toString() {
result = this.getKind() + " " + this.getName()
or
/* No name is defined, which means that this module is not on an import path. So it must be a script */
not exists(this.getName()) and not this.isPackage() and
not exists(this.getName()) and
not this.isPackage() and
result = "Script " + this.getFile().getShortName()
or
/* Package missing name, so just use the path instead */
not exists(this.getName()) and this.isPackage() and
result = "Package at " + this.getPath().getAbsolutePath()
not exists(this.getName()) and
this.isPackage() and
result = "Package at " + this.getPath().getAbsolutePath()
}
/** This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
* The enclosing scope of this module (always none) */
override Scope getScope() {
none()
}
/**
* This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
* The enclosing scope of this module (always none)
*/
override Scope getScope() { none() }
/** The enclosing scope of this module (always none) */
override Scope getEnclosingScope() {
none()
}
override Scope getEnclosingScope() { none() }
/** Gets the statements forming the body of this module */
override StmtList getBody() {
result = Module_.super.getBody()
}
override StmtList getBody() { result = Module_.super.getBody() }
/** Gets the nth statement of this module */
override Stmt getStmt(int n) {
result = Module_.super.getStmt(n)
}
override Stmt getStmt(int n) { result = Module_.super.getStmt(n) }
/** Gets a top-level statement in this module */
override Stmt getAStmt() {
result = Module_.super.getAStmt()
}
override Stmt getAStmt() { result = Module_.super.getAStmt() }
/** Gets the name of this module */
override string getName() {
@@ -54,15 +49,13 @@ class Module extends Module_, Scope, AstNode {
/** Gets the short name of the module. For example the short name of module x.y.z is 'z' */
string getShortName() {
result = this.getName().suffix(this.getPackage().getName().length()+1)
result = this.getName().suffix(this.getPackage().getName().length() + 1)
or
result = this.getName() and not exists(this.getPackage())
}
/** Gets this module */
override Module getEnclosingModule() {
result = this
}
override Module getEnclosingModule() { result = this }
/** Gets the __init__ module of this module if the module is a package and it has an __init__ module */
Module getInitModule() {
@@ -70,34 +63,25 @@ class Module extends Module_, Scope, AstNode {
}
/** Whether this module is a package initializer */
predicate isPackageInit() {
this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage()
}
predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() }
/** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */
/** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */
string getAnExport() {
py_exports(this, result)
or
exists(ModuleObjectInternal mod |
mod.getSource() = this.getEntryNode() |
exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() |
mod.(ModuleValue).exports(result)
)
}
/** Gets the source file for this module */
File getFile() {
py_module_path(this, result)
}
File getFile() { py_module_path(this, result) }
/** Gets the source file or folder for this module or package */
Container getPath() {
py_module_path(this, result)
}
Container getPath() { py_module_path(this, result) }
/** Whether this is a package */
predicate isPackage() {
this.getPath() instanceof Folder
}
predicate isPackage() { this.getPath() instanceof Folder }
/** Gets the package containing this module (or parent package if this is a package) */
Module getPackage() {
@@ -112,15 +96,13 @@ class Module extends Module_, Scope, AstNode {
}
/** Gets the metrics for this module */
ModuleMetrics getMetrics() {
result = this
}
ModuleMetrics getMetrics() { result = this }
/** Use ModuleObject.getAnImportedModule() instead.
* Gets a module imported by this module */
deprecated Module getAnImportedModule() {
result.getName() = this.getAnImportedModuleName()
}
/**
* Use ModuleObject.getAnImportedModule() instead.
* Gets a module imported by this module
*/
deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() }
string getAnImportedModuleName() {
exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName())
@@ -142,22 +124,23 @@ class Module extends Module_, Scope, AstNode {
}
/** Whether name is declared in the __all__ list of this module */
predicate declaredInAll(string name)
{
predicate declaredInAll(string name) {
exists(AssignStmt a, GlobalVariable all |
a.defines(all) and a.getScope() = this and
all.getId() = "__all__" and ((List)a.getValue()).getAnElt().(StrConst).getText() = name
a.defines(all) and
a.getScope() = this and
all.getId() = "__all__" and
a.getValue().(List).getAnElt().(StrConst).getText() = name
)
}
override AstNode getAChildNode() {
result = this.getAStmt()
}
override AstNode getAChildNode() { result = this.getAStmt() }
predicate hasFromFuture(string attr) {
exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name |
im.getModule() = ie and ie.getName() = "__future__" and
a.getAsname() = name and name.getId() = attr and
im.getModule() = ie and
ie.getName() = "__future__" and
a.getAsname() = name and
name.getId() = attr and
i.getASubExpression() = im and
i.getAName() = a and
i.getEnclosingModule() = this
@@ -165,55 +148,40 @@ class Module extends Module_, Scope, AstNode {
}
/** Gets the path element from which this module was loaded. */
Container getLoadPath() {
result = this.getPath().getImportRoot()
}
Container getLoadPath() { result = this.getPath().getImportRoot() }
/** Holds if this module is in the standard library for version `major.minor` */
predicate inStdLib(int major, int minor) {
this.getLoadPath().isStdLibRoot(major, minor)
}
predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) }
/** Holds if this module is in the standard library */
predicate inStdLib() {
this.getLoadPath().isStdLibRoot()
}
predicate inStdLib() { this.getLoadPath().isStdLibRoot() }
override
predicate containsInScope(AstNode inner) {
Scope.super.containsInScope(inner)
}
override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) }
override
predicate contains(AstNode inner) {
Scope.super.contains(inner)
}
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
/** Gets the kind of this module. */
override string getKind() {
if this.isPackage() then
result = "Package"
if this.isPackage()
then result = "Package"
else (
not exists(Module_.super.getKind()) and result = "Module"
or
result = Module_.super.getKind()
)
}
}
bindingset[name]
private predicate legalDottedName(string name) {
name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*")
}
bindingset[name]
private predicate legalShortName(string name) {
name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*")
}
private predicate legalShortName(string name) { name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*") }
/** Holds if `f` is potentially a source package.
/**
* Holds if `f` is potentially a source package.
* Does it have an __init__.py file (or --respect-init=False for Python 2) and is it within the source archive?
*/
private predicate isPotentialSourcePackage(Folder f) {
@@ -240,8 +208,12 @@ string moduleNameFromFile(Container file) {
result = moduleNameFromFile(file.getParent()) + "." + basename
)
or
isPotentialSourcePackage(file) and result = file.getStem() and
(not isPotentialSourcePackage(file.getParent()) or not legalShortName(file.getParent().getBaseName()))
isPotentialSourcePackage(file) and
result = file.getStem() and
(
not isPotentialSourcePackage(file.getParent()) or
not legalShortName(file.getParent().getBaseName())
)
or
result = file.getStem() and file.getParent() = file.getImportRoot()
or
@@ -253,8 +225,8 @@ private predicate isStubRoot(Folder f) {
f.getAbsolutePath().matches("%/data/python/stubs")
}
/** Holds if the Container `c` should be the preferred file or folder for
/**
* Holds if the Container `c` should be the preferred file or folder for
* the given name when performing imports.
* Trivially true for any container if it is the only one with its name.
* However, if there are several modules with the same name, then

View File

@@ -2,123 +2,86 @@ import python
/** Base class for operators */
class Operator extends Operator_ {
/** Gets the name of the special method used to implement this operator */
string getSpecialMethodName() { none() }
}
/* Unary Expression and its operators */
/** A unary expression: (`+x`), (`-x`) or (`~x`) */
class UnaryExpr extends UnaryExpr_ {
override Expr getASubExpression() {
result = this.getOperand()
}
override Expr getASubExpression() { result = this.getOperand() }
}
/** A unary operator: `+`, `-`, `~` or `not` */
class Unaryop extends Unaryop_ {
/** Gets the name of the special method used to implement this operator */
string getSpecialMethodName() { none() }
}
/** An invert (`~`) unary operator */
class Invert extends Invert_ {
override string getSpecialMethodName() { result = "__invert__" }
}
/** A positive (`+`) unary operator */
/** A positive (`+`) unary operator */
class UAdd extends UAdd_ {
override string getSpecialMethodName() { result = "__pos__" }
}
/** A negation (`-`) unary operator */
class USub extends USub_ {
override string getSpecialMethodName() { result = "__neg__" }
}
/** A `not` unary operator */
class Not extends Not_ {
override string getSpecialMethodName() { none() }
}
/* Binary Operation and its operators */
/** A binary expression, such as `x + y` */
class BinaryExpr extends BinaryExpr_ {
override Expr getASubExpression() {
result = this.getLeft() or result = this.getRight()
}
override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() }
}
/** A power (`**`) binary operator */
class Pow extends Pow_ {
override string getSpecialMethodName() { result = "__pow__" }
}
/** A right shift (`>>`) binary operator */
class RShift extends RShift_ {
override string getSpecialMethodName() { result = "__rshift__" }
}
/** A subtract (`-`) binary operator */
class Sub extends Sub_ {
override string getSpecialMethodName() { result = "__sub__" }
}
/** A bitwise and (`&`) binary operator */
class BitAnd extends BitAnd_ {
override string getSpecialMethodName() { result = "__and__" }
}
/** A bitwise or (`|`) binary operator */
class BitOr extends BitOr_ {
override string getSpecialMethodName() { result = "__or__" }
}
/** A bitwise exclusive-or (`^`) binary operator */
class BitXor extends BitXor_ {
override string getSpecialMethodName() { result = "__xor__" }
}
/** An add (`+`) binary operator */
class Add extends Add_ {
override string getSpecialMethodName() { result = "__add__" }
}
/** An (true) divide (`/`) binary operator */
class Div extends Div_ {
override string getSpecialMethodName() {
override string getSpecialMethodName() {
result = "__truediv__"
or
major_version() = 2 and result = "__div__"
@@ -127,185 +90,123 @@ class Div extends Div_ {
/** An floor divide (`//`) binary operator */
class FloorDiv extends FloorDiv_ {
override string getSpecialMethodName() { result = "__floordiv__" }
}
/** A left shift (`<<`) binary operator */
class LShift extends LShift_ {
override string getSpecialMethodName() { result = "__lshift__" }
}
/** A modulo (`%`) binary operator, which includes string formatting */
class Mod extends Mod_ {
override string getSpecialMethodName() { result = "__mod__" }
}
/** A multiplication (`*`) binary operator */
class Mult extends Mult_ {
override string getSpecialMethodName() { result = "__mul__" }
}
/** A matrix multiplication (`@`) binary operator */
class MatMult extends MatMult_ {
override string getSpecialMethodName() { result = "__matmul__" }
}
/* Comparison Operation and its operators */
/** A comparison operation, such as `x<y` */
class Compare extends Compare_ {
override Expr getASubExpression() { result = this.getLeft() or result = this.getAComparator() }
override Expr getASubExpression() {
result = this.getLeft() or result = this.getAComparator()
}
/** Whether as part of this comparison 'left' is compared with 'right' using the operator 'op'.
* For example, the comparison `a<b<c` compares(`a`, `b`, `<`) and compares(`b`, `c`, `<`). */
predicate compares(Expr left, Cmpop op, Expr right)
{
/**
* Whether as part of this comparison 'left' is compared with 'right' using the operator 'op'.
* For example, the comparison `a<b<c` compares(`a`, `b`, `<`) and compares(`b`, `c`, `<`).
*/
predicate compares(Expr left, Cmpop op, Expr right) {
this.getLeft() = left and this.getComparator(0) = right and op = this.getOp(0)
or
exists(int n | this.getComparator(n) = left and this.getComparator(n+1) = right and op = this.getOp(n+1))
exists(int n |
this.getComparator(n) = left and this.getComparator(n + 1) = right and op = this.getOp(n + 1)
)
}
}
/** List of comparison operators in a comparison */
class CmpopList extends CmpopList_ {
}
class CmpopList extends CmpopList_ { }
/** A comparison operator */
abstract class Cmpop extends Cmpop_ {
string getSymbol() {
none()
}
string getSymbol() { none() }
string getSpecialMethodName() { none() }
}
/** A greater than (`>`) comparison operator */
class Gt extends Gt_ {
override string getSymbol() {
result = ">"
}
override string getSymbol() { result = ">" }
override string getSpecialMethodName() { result = "__gt__" }
}
/** A greater than or equals (`>=`) comparison operator */
class GtE extends GtE_ {
override string getSymbol() {
result = ">="
}
override string getSymbol() { result = ">=" }
override string getSpecialMethodName() { result = "__ge__" }
}
/** An `in` comparison operator */
class In extends In_ {
override string getSymbol() {
result = "in"
}
override string getSymbol() { result = "in" }
}
/** An `is` comparison operator */
class Is extends Is_ {
override string getSymbol() {
result = "is"
}
override string getSymbol() { result = "is" }
}
/** An `is not` comparison operator */
class IsNot extends IsNot_ {
override string getSymbol() {
result = "is not"
}
override string getSymbol() { result = "is not" }
}
/** An equals (`==`) comparison operator */
class Eq extends Eq_ {
override string getSymbol() {
result = "=="
}
override string getSymbol() { result = "==" }
override string getSpecialMethodName() { result = "__eq__" }
}
/** A less than (`<`) comparison operator */
class Lt extends Lt_ {
override string getSymbol() {
result = "<"
}
override string getSymbol() { result = "<" }
override string getSpecialMethodName() { result = "__lt__" }
}
/** A less than or equals (`<=`) comparison operator */
class LtE extends LtE_ {
override string getSymbol() {
result = "<="
}
override string getSymbol() { result = "<=" }
override string getSpecialMethodName() { result = "__le__" }
}
/** A not equals (`!=`) comparison operator */
class NotEq extends NotEq_ {
override string getSymbol() {
result = "!="
}
override string getSymbol() { result = "!=" }
override string getSpecialMethodName() { result = "__ne__" }
}
/** An `not in` comparison operator */
class NotIn extends NotIn_ {
override string getSymbol() {
result = "not in"
}
override string getSymbol() { result = "not in" }
}
/* Boolean Operation (and/or) and its operators */
/** A boolean shortcut (and/or) operation */
class BoolExpr extends BoolExpr_ {
override Expr getASubExpression() {
result = this.getAValue()
}
override Expr getASubExpression() { result = this.getAValue() }
string getOperator() {
this.getOp() instanceof And and result = "and"
@@ -315,30 +216,24 @@ class BoolExpr extends BoolExpr_ {
/** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */
predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) {
if this.getOp() instanceof And then (
if this.getOp() instanceof And
then (
wholeIsTrue = true and partIsTrue = true and part = this.getAValue()
or
wholeIsTrue = true and ((BoolExpr)this.getAValue()).impliesValue(part, partIsTrue, true)
wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true)
) else (
wholeIsTrue = false and partIsTrue = false and part = this.getAValue()
or
wholeIsTrue = false and ((BoolExpr)this.getAValue()).impliesValue(part, partIsTrue, false)
wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false)
)
}
}
/** A short circuit boolean operator, and/or */
class Boolop extends Boolop_ {
}
class Boolop extends Boolop_ { }
/** An `and` boolean operator */
class And extends And_ {
}
class And extends And_ { }
/** An `or` boolean operator */
class Or extends Or_ {
}
class Or extends Or_ { }

View File

@@ -2,40 +2,33 @@
import python
/** A single static assignment variable.
/**
* A single static assignment variable.
* An SSA variable is a variable which is only assigned once (statically).
* SSA variables can be defined as normal variables or by a phi node which can occur at joins in the flow graph.
* Definitions without uses do not have a SSA variable.
*/
class SsaVariable extends @py_ssa_var{
SsaVariable() {
py_ssa_var(this, _)
}
class SsaVariable extends @py_ssa_var {
SsaVariable() { py_ssa_var(this, _) }
/** Gets the source variable */
Variable getVariable() {
py_ssa_var(this, result)
}
Variable getVariable() { py_ssa_var(this, result) }
/** Gets a use of this variable */
ControlFlowNode getAUse() {
py_ssa_use(result, this)
}
ControlFlowNode getAUse() { py_ssa_use(result, this) }
/** Gets the definition (which may be a deletion) of this SSA variable */
ControlFlowNode getDefinition() {
py_ssa_defn(this, result)
}
ControlFlowNode getDefinition() { py_ssa_defn(this, result) }
/** Gets an argument of the phi function defining this variable.
/**
* Gets an argument of the phi function defining this variable.
* This predicate uses the raw SSA form produced by the extractor.
* In general, you should use `getAPrunedPhiInput()` instead. */
SsaVariable getAPhiInput() {
py_ssa_phi(this, result)
}
* In general, you should use `getAPrunedPhiInput()` instead.
*/
SsaVariable getAPhiInput() { py_ssa_phi(this, result) }
/** Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable.
/**
* Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable.
* For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables
* for this phi-node is live. This predicate returns the predecessor block such that the variable 'input'
* is the live variable on the edge result->B.
@@ -44,23 +37,25 @@ class SsaVariable extends @py_ssa_var{
input = this.getAPhiInput() and
result = this.getAPredecessorBlockForPhi() and
input.getDefinition().getBasicBlock().dominates(result) and
/* Beware the case where an SSA variable that is an input on one edge dominates another edge.
/*
* Beware the case where an SSA variable that is an input on one edge dominates another edge.
* Consider (in SSA form):
* x0 = 0
* if cond:
* x1 = 1
* x2 = phi(x0, x1)
* use(x2)
*
*
* The definition of x0 dominates the exit from the block x1=1, even though it does not reach it.
* Hence we need to check that no other definition dominates the edge and actually reaches it.
* Note that if a dominates c and b dominates c, then either a dominates b or vice-versa.
*/
not exists(SsaVariable other, BasicBlock other_def |
not other = input and
other = this.getAPhiInput() and
other_def = other.getDefinition().getBasicBlock()
|
|
other_def.dominates(result) and
input.getDefinition().getBasicBlock().strictlyDominates(other_def)
)
@@ -81,22 +76,16 @@ class SsaVariable extends @py_ssa_var{
result = this.getAPhiInput().getAnUltimateDefinition()
}
string toString() {
result = "SSA Variable " + this.getId()
}
string toString() { result = "SSA Variable " + this.getId() }
Location getLocation() {
result = this.getDefinition().getLocation()
}
Location getLocation() { result = this.getDefinition().getLocation() }
/** Gets the id (name) of this variable */
string getId() {
result = this.getVariable().getId()
}
string getId() { result = this.getVariable().getId() }
/** Gets the incoming edges for a Phi node. */
private BasicBlock getAPredecessorBlockForPhi() {
exists(getAPhiInput()) and
exists(getAPhiInput()) and
result.getASuccessor() = this.getDefinition().getBasicBlock()
}
@@ -112,11 +101,13 @@ class SsaVariable extends @py_ssa_var{
or
exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition())
or
/* For phi-nodes, there must be a corresponding phi-input for each control-flow
/*
* For phi-nodes, there must be a corresponding phi-input for each control-flow
* predecessor. Otherwise, the variable will be undefined on that incoming edge.
* WARNING: the same phi-input may cover multiple predecessors, so this check
* cannot be done by counting.
*/
exists(BasicBlock incoming |
incoming = this.getAPredecessorBlockForPhi() and
not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
@@ -131,11 +122,13 @@ class SsaVariable extends @py_ssa_var{
or
exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined())
or
/* For phi-nodes, there must be a corresponding phi-input for each control-flow
/*
* For phi-nodes, there must be a corresponding phi-input for each control-flow
* predecessor. Otherwise, the variable will be undefined on that incoming edge.
* WARNING: the same phi-input may cover multiple predecessors, so this check
* cannot be done by counting.
*/
exists(BasicBlock incoming |
reaches_end(incoming) and
incoming = this.getAPrunedPredecessorBlockForPhi() and
@@ -144,14 +137,17 @@ class SsaVariable extends @py_ssa_var{
}
private predicate implicitlyDefined() {
not exists(this.getDefinition()) and not py_ssa_phi(this, _) and
not exists(this.getDefinition()) and
not py_ssa_phi(this, _) and
exists(GlobalVariable var | this.getVariable() = var |
globallyDefinedName(var.getId()) or
var.getId() = "__path__" and ((Module)var.getScope()).isPackageInit()
globallyDefinedName(var.getId())
or
var.getId() = "__path__" and var.getScope().(Module).isPackageInit()
)
}
/** Gets the global variable that is accessed if this local is undefined.
/**
* Gets the global variable that is accessed if this local is undefined.
* Only applies to local variables in class scopes.
*/
GlobalVariable getFallbackGlobal() {
@@ -163,28 +159,26 @@ class SsaVariable extends @py_ssa_var{
)
}
/* Whether this SSA variable is the first parameter of a method
/*
* Whether this SSA variable is the first parameter of a method
* (regardless of whether it is actually called self or not)
*/
predicate isSelf() {
exists(Function func |
func.isMethod()
and
exists(Function func |
func.isMethod() and
this.getDefinition().getNode() = func.getArg(0)
)
}
}
private predicate reaches_end(BasicBlock b) {
not exits_early(b)
and
not exits_early(b) and
(
/* Entry point */
not exists(BasicBlock prev | prev.getASuccessor() = b)
or
exists(BasicBlock prev | prev.getASuccessor() = b |
reaches_end(prev)
)
/* Entry point */
not exists(BasicBlock prev | prev.getASuccessor() = b)
or
exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev))
)
}
@@ -209,32 +203,19 @@ private predicate builtin_constant(string name) {
}
private predicate auto_name(string name) {
name = "__file__" or name = "__builtins__" or name = "__name__"
name = "__file__" or name = "__builtins__" or name = "__name__"
}
/** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */
predicate globallyDefinedName(string name) {
builtin_constant(name) or auto_name(name)
}
predicate globallyDefinedName(string name) { builtin_constant(name) or auto_name(name) }
/** An SSA variable that is backed by a global variable */
class GlobalSsaVariable extends EssaVariable {
GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable }
GlobalSsaVariable() {
this.getSourceVariable() instanceof GlobalVariable
}
GlobalVariable getVariable() {
result = this.getSourceVariable()
}
string getId() {
result = this.getVariable().getId()
}
override string toString() {
result = "GSSA Variable " + this.getId()
}
GlobalVariable getVariable() { result = this.getSourceVariable() }
string getId() { result = this.getVariable().getId() }
override string toString() { result = "GSSA Variable " + this.getId() }
}

View File

@@ -1,80 +1,58 @@
import python
/** A Scope. A scope is the lexical extent over which all identifiers with the same name refer to the same variable.
/**
* A Scope. A scope is the lexical extent over which all identifiers with the same name refer to the same variable.
* Modules, Classes and Functions are all Scopes. There are no other scopes.
* The scopes for expressions that create new scopes, lambdas and comprehensions, are handled by creating an anonymous Function. */
* The scopes for expressions that create new scopes, lambdas and comprehensions, are handled by creating an anonymous Function.
*/
class Scope extends Scope_ {
Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() }
Module getEnclosingModule() {
result = this.getEnclosingScope().getEnclosingModule()
}
/** This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
/**
* This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
* The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an
* `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that
* the apparent semantics and the actual semantics coincide.
* [ Gets the scope enclosing this scope (modules have no enclosing scope) ]
*/
Scope getScope() {
none()
}
Scope getScope() { none() }
/** Gets the scope enclosing this scope (modules have no enclosing scope) */
Scope getEnclosingScope() {
none()
}
Scope getEnclosingScope() { none() }
/** Gets the statements forming the body of this scope */
StmtList getBody() {
none()
}
StmtList getBody() { none() }
/** Gets the nth statement of this scope */
Stmt getStmt(int n) {
none()
}
Stmt getStmt(int n) { none() }
/** Gets a top-level statement in this scope */
Stmt getAStmt() {
none()
}
Stmt getAStmt() { none() }
Location getLocation() {
none()
}
Location getLocation() { none() }
/** Gets the name of this scope */
string getName() {
py_strs(result, this, 0)
}
string getName() { py_strs(result, this, 0) }
/** Gets the docstring for this scope */
StrConst getDocString() {
result = ((ExprStmt)this.getStmt(0)).getValue()
}
StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() }
/** Gets the entry point into this Scope's control flow graph */
ControlFlowNode getEntryNode() {
py_scope_flow(result, this, -1)
}
ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) }
/** Gets the non-explicit exit from this Scope's control flow graph */
ControlFlowNode getFallthroughNode() {
py_scope_flow(result, this, 0)
}
ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) }
/** Gets the exit of this scope following from a return statement */
ControlFlowNode getReturnNode() {
py_scope_flow(result, this, 2)
}
ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) }
/** Gets an exit from this Scope's control flow graph */
ControlFlowNode getAnExitNode() {
exists (int i | py_scope_flow(result, this, i) and i >= 0)
}
ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) }
/** Gets an exit from this Scope's control flow graph,
* that does not result from an exception */
/**
* Gets an exit from this Scope's control flow graph,
* that does not result from an exception
*/
ControlFlowNode getANormalExit() {
result = this.getFallthroughNode()
or
@@ -82,9 +60,7 @@ class Scope extends Scope_ {
}
/** Holds if this a top-level (non-nested) class or function */
predicate isTopLevel() {
this.getEnclosingModule() = this.getEnclosingScope()
}
predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() }
/** Holds if this scope is deemed to be public */
predicate isPublic() {
@@ -93,10 +69,9 @@ class Scope extends Scope_ {
/* Not implicitly private */
this.getName().charAt(0) != "_" and
(
this instanceof Module
this instanceof Module
or
exists(Module m |
m = this.getEnclosingScope() and m.isPublic() |
exists(Module m | m = this.getEnclosingScope() and m.isPublic() |
/* If the module has an __all__, is this in it */
not exists(m.getAnExport())
or
@@ -116,66 +91,63 @@ class Scope extends Scope_ {
exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a))
}
/** Holds if this scope can be expected to execute before `other`.
/**
* Holds if this scope can be expected to execute before `other`.
* Modules precede functions and methods in those modules
* `__init__` precedes other methods. `__enter__` precedes `__exit__`.
* NOTE that this is context-insensitive, so a module "precedes" a function
* NOTE that this is context-insensitive, so a module "precedes" a function
* in that module, even if that function is called from the module scope.
*/
predicate precedes(Scope other) {
exists(Function f, string name |
f = other and name = f.getName() |
if f.isMethod() then (
exists(Function f, string name | f = other and name = f.getName() |
if f.isMethod()
then
// The __init__ method is preceded by the enclosing module
this = f.getEnclosingModule() and name = "__init__"
or
exists(Class c, string pred_name |
// __init__ -> __enter__ -> __exit__
// __init__ -> other-methods
f.getScope() = c and (
f.getScope() = c and
(
pred_name = "__init__" and not name = "__init__" and not name = "__exit__"
or
pred_name = "__enter__" and name = "__exit__"
)
|
|
this.getScope() = c and
pred_name = this.(Function).getName()
or
not exists(Function pre_func |
pre_func.getName() = pred_name and
pre_func.getScope() = c
) and this = other.getEnclosingModule()
) and
this = other.getEnclosingModule()
)
) else (
else
// Normal functions are preceded by the enclosing module
this = f.getEnclosingModule()
)
)
}
/** Gets the evaluation scope for code in this (lexical) scope.
/**
* Gets the evaluation scope for code in this (lexical) scope.
* This is usually the scope itself, but may be an enclosing scope.
* Notably, for list comprehensions in Python 2.
*/
Scope getEvaluatingScope() {
result = this
}
Scope getEvaluatingScope() { result = this }
/** Holds if this scope is in the source archive,
/**
* Holds if this scope is in the source archive,
* that is it is part of the code specified, not library code
*/
predicate inSource() {
exists(this.getEnclosingModule().getFile().getRelativePath())
}
predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) }
Stmt getLastStatement() {
result = this.getBody().getLastItem().getLastStatement()
}
Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() }
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
predicate containsInScope(AstNode inner) {
this.getBody().contains(inner) and
this = inner.getScope()
}
}

View File

@@ -1,23 +1,19 @@
/** Utilities to support queries about instance attribute accesses of
/**
* Utilities to support queries about instance attribute accesses of
* the form `self.attr`.
*/
import python
private import semmle.python.pointsto.Filters
/** An attribute access where the left hand side of the attribute expression
* is `self`.
*/
/**
* An attribute access where the left hand side of the attribute expression
* is `self`.
*/
class SelfAttribute extends Attribute {
SelfAttribute() { self_attribute(this, _) }
SelfAttribute() {
self_attribute(this, _)
}
Class getClass() {
self_attribute(this, result)
}
Class getClass() { self_attribute(this, result) }
}
/** Whether variable 'self' is the self variable in method 'method' */
@@ -29,8 +25,7 @@ private predicate self_variable(Function method, Variable self) {
/** Whether attribute is an access of the form `self.attr` in the body of the class 'cls' */
private predicate self_attribute(Attribute attr, Class cls) {
exists(Function f, Variable self |
self_variable(f, self) |
exists(Function f, Variable self | self_variable(f, self) |
self.getAnAccess() = attr.getObject() and
cls = f.getScope+()
)
@@ -38,14 +33,12 @@ private predicate self_attribute(Attribute attr, Class cls) {
/** Helper class for UndefinedClassAttribute.ql &amp; MaybeUndefinedClassAttribute.ql */
class SelfAttributeRead extends SelfAttribute {
SelfAttributeRead() {
this.getCtx() instanceof Load and
// Be stricter for loads.
// Be stricter for loads.
// We want to generous as to what is defined (i.e. stores),
// but strict as to what needs to be defined (i.e. loads).
exists(ClassObject cls, FunctionObject func |
cls.declaredAttribute(_) = func |
exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func |
func.getFunction() = this.getScope() and
cls.getPyClass() = this.getClass()
)
@@ -59,35 +52,28 @@ class SelfAttributeRead extends SelfAttribute {
)
}
pragma [noinline] predicate locallyDefined() {
pragma[noinline]
predicate locallyDefined() {
exists(SelfAttributeStore store |
this.getName() = store.getName() and
this.getScope() = store.getScope() |
this.getName() = store.getName() and
this.getScope() = store.getScope()
|
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
)
}
}
class SelfAttributeStore extends SelfAttribute {
SelfAttributeStore() { this.getCtx() instanceof Store }
SelfAttributeStore() {
this.getCtx() instanceof Store
}
Expr getAssignedValue() {
exists(Assign a | a.getATarget() = this |
result = a.getValue()
)
}
Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) }
}
private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) {
exists(SsaVariable param |
method.getFunction().getArg(n).asName() = param.getDefinition().getNode()
|
exists(AttrNode attr |
|
exists(AttrNode attr |
attr.getObject(name) = param.getAUse() and
attr.isStore()
)

View File

@@ -2,34 +2,21 @@ import python
/** A statement */
class Stmt extends Stmt_, AstNode {
/** Gets the scope immediately enclosing this statement */
override Scope getScope() {
py_scopes(this, result)
}
override Scope getScope() { py_scopes(this, result) }
override string toString() {
result = "Stmt"
}
override string toString() { result = "Stmt" }
/** Gets the module enclosing this statement */
Module getEnclosingModule() {
result = this.getScope().getEnclosingModule()
}
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
override Location getLocation() {
result = Stmt_.super.getLocation()
}
override Location getLocation() { result = Stmt_.super.getLocation() }
/** Gets an immediate (non-nested) sub-expression of this statement */
Expr getASubExpression() {
none()
}
Expr getASubExpression() { none() }
/** Gets an immediate (non-nested) sub-statement of this statement */
Stmt getASubStatement() {
none()
}
Stmt getASubStatement() { none() }
override AstNode getAChildNode() {
result = this.getASubExpression()
@@ -42,8 +29,8 @@ class Stmt extends Stmt_, AstNode {
this.containsInScope(result.getNode())
}
/** Gets a control flow node for an entry into this statement.
/**
* Gets a control flow node for an entry into this statement.
*/
ControlFlowNode getAnEntryNode() {
result = this.possibleEntryNode() and
@@ -60,131 +47,94 @@ class Stmt extends Stmt_, AstNode {
exists(If ifstmt |
ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this)
or
ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and ifstmt.getOrelse().contains(this)
ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and
ifstmt.getOrelse().contains(this)
)
or
exists(While whilestmt |
whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and whilestmt.getBody().contains(this)
whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and
whilestmt.getBody().contains(this)
)
}
/** Gets the final statement in this statement, ordered by location.
/**
* Gets the final statement in this statement, ordered by location.
* Will be this statement if not a compound statement.
*/
Stmt getLastStatement() {
result = this
}
Stmt getLastStatement() { result = this }
}
/** A statement that includes a binding (except imports) */
class Assign extends Assign_ {
/** Use ControlFlowNodes and SsaVariables for data-flow analysis. */
predicate defines(Variable v) {
this.getATarget().defines(v)
}
predicate defines(Variable v) { this.getATarget().defines(v) }
override Expr getASubExpression() {
result = this.getATarget() or
result = this.getValue()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** An assignment statement */
class AssignStmt extends Assign {
/* syntax: Expr, ... = Expr */
AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef }
AssignStmt() {
not this instanceof FunctionDef and not this instanceof ClassDef
}
override string toString() {
result = "AssignStmt"
}
override string toString() { result = "AssignStmt" }
}
/** An augmented assignment statement, such as `x += y` */
class AugAssign extends AugAssign_ {
/* syntax: Expr += Expr */
override Expr getASubExpression() { result = this.getOperation() }
override Expr getASubExpression() {
result = this.getOperation()
}
Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() }
Expr getTarget() {
result = ((BinaryExpr)this.getOperation()).getLeft()
}
Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() }
Expr getValue() {
result = ((BinaryExpr)this.getOperation()).getRight()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** An annotated assignment statement, such as `x: int = 0` */
class AnnAssign extends AnnAssign_ {
/* syntax: Expr: Expr = Expr */
override Expr getASubExpression() {
result = this.getAnnotation() or
result = this.getTarget() or
result = this.getValue()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
/** Holds if the value of the annotation of this assignment is stored at runtime. */
predicate isStored() {
not this.getScope() instanceof Function
and
not this.getScope() instanceof Function and
exists(Name n |
n = this.getTarget()
and
n = this.getTarget() and
not n.isParenthesized()
)
}
}
/** An exec statement */
class Exec extends Exec_ {
/* syntax: exec Expr */
override Expr getASubExpression() {
result = this.getBody() or
result = this.getGlobals() or
result = this.getLocals()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** An except statement (part of a `try` statement), such as `except IOError as err:` */
class ExceptStmt extends ExceptStmt_ {
/* syntax: except Expr [ as Expr ]: */
/** Gets the immediately enclosing try statement */
Try getTry() {
result.getAHandler() = this
}
Try getTry() { result.getAHandler() = this }
override Expr getASubExpression() {
result = this.getName()
@@ -192,96 +142,54 @@ class ExceptStmt extends ExceptStmt_ {
result = this.getType()
}
override Stmt getASubStatement() {
result = this.getAStmt()
}
override Stmt getLastStatement() {
result = this.getBody().getLastItem().getLastStatement()
}
override Stmt getASubStatement() { result = this.getAStmt() }
override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() }
}
/** An assert statement, such as `assert a == b, "A is not equal to b"` */
class Assert extends Assert_ {
/* syntax: assert Expr [, Expr] */
override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() }
override Expr getASubExpression() {
result = this.getMsg() or result = this.getTest()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** A break statement */
class Break extends Break_ {
/* syntax: assert Expr [, Expr] */
override Expr getASubExpression() { none() }
override Expr getASubExpression() {
none()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** A continue statement */
class Continue extends Continue_ {
/* syntax: continue */
override Expr getASubExpression() { none() }
override Expr getASubExpression() {
none()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** A delete statement, such as `del x[-1]` */
class Delete extends Delete_ {
/* syntax: del Expr, ... */
override Expr getASubExpression() { result = this.getATarget() }
override Expr getASubExpression() {
result = this.getATarget()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** An expression statement, such as `len(x)` or `yield y` */
class ExprStmt extends ExprStmt_ {
/* syntax: Expr */
override Expr getASubExpression() { result = this.getValue() }
override Expr getASubExpression() {
result = this.getValue()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** A for statement, such as `for x in y: print(x)` */
class For extends For_ {
/* syntax: for varname in Expr: ... */
override Stmt getASubStatement() {
result = this.getAStmt() or
result = this.getAnOrelse()
@@ -292,39 +200,26 @@ class For extends For_ {
result = this.getIter()
}
override Stmt getLastStatement() {
result = this.getBody().getLastItem().getLastStatement()
}
override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() }
}
/** A global statement, such as `global var` */
class Global extends Global_ {
/* syntax: global varname */
override Expr getASubExpression() { none() }
override Expr getASubExpression() {
none()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
/** An if statement, such as `if eggs: print("spam")` */
class If extends If_ {
/* syntax: if Expr: ... */
override Stmt getASubStatement() {
result = this.getAStmt() or
result = this.getAnOrelse()
}
override Expr getASubExpression() {
result = this.getTest()
}
override Expr getASubExpression() { result = this.getTest() }
/** Whether this if statement takes the form `if __name__ == "__main__":` */
predicate isNameEqMain() {
@@ -343,11 +238,13 @@ class If extends If_ {
/** Whether this if statement starts with the keyword `elif` */
predicate isElif() {
/* The Python parser turns all elif chains into nested if-else statements.
/*
* The Python parser turns all elif chains into nested if-else statements.
* An `elif` can be identified as it is the first statement in an `else` block
* and it is not indented relative to its parent `if`.
*/
exists(If i |
exists(If i |
i.getOrelse(0) = this and
this.getLocation().getStartColumn() = i.getLocation().getStartColumn()
)
@@ -365,74 +262,49 @@ class If extends If_ {
not exists(this.getOrelse()) and
result = this.getBody().getLastItem().getLastStatement()
}
}
/** A nonlocal statement, such as `nonlocal var` */
class Nonlocal extends Nonlocal_ {
/* syntax: nonlocal varname */
override Stmt getASubStatement() { none() }
override Stmt getASubStatement() {
none()
}
override Expr getASubExpression() {
none()
}
override Expr getASubExpression() { none() }
Variable getAVariable() {
result.getScope() = this.getScope() and
result.getId() = this.getAName()
}
}
/** A pass statement */
class Pass extends Pass_ {
/* syntax: pass */
override Stmt getASubStatement() { none() }
override Stmt getASubStatement() {
none()
}
override Expr getASubExpression() {
none()
}
override Expr getASubExpression() { none() }
}
/** A print statement (Python 2 only), such as `print 0` */
class Print extends Print_ {
/* syntax: print Expr, ... */
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
override Expr getASubExpression() {
result = this.getAValue() or
result = this.getDest()
}
}
/** A raise statement, such as `raise CompletelyDifferentException()` */
class Raise extends Raise_ {
/* syntax: raise Expr */
override Stmt getASubStatement() { none() }
override Stmt getASubStatement() {
none()
}
override Expr getASubExpression() { py_exprs(result, _, this, _) }
override Expr getASubExpression() {
py_exprs(result, _, this, _)
}
/** The expression immediately following the `raise`, this is the
/**
* The expression immediately following the `raise`, this is the
* exception raised, but not accounting for tuples in Python 2.
*/
Expr getException() {
@@ -442,12 +314,10 @@ class Raise extends Raise_ {
}
/** The exception raised, accounting for tuples in Python 2. */
Expr getRaised()
{
exists(Expr raw |
raw = this.getException() |
if (not major_version() = 2 or not exists(raw.(Tuple).getAnElt())) then
result = raw
Expr getRaised() {
exists(Expr raw | raw = this.getException() |
if not major_version() = 2 or not exists(raw.(Tuple).getAnElt())
then result = raw
else
/* In Python 2 raising a tuple will result in the first element of the tuple being raised. */
result = raw.(Tuple).getElt(0)
@@ -457,27 +327,16 @@ class Raise extends Raise_ {
/** A return statement, such as return None */
class Return extends Return_ {
/* syntax: return Expr */
override Stmt getASubStatement() { none() }
override Stmt getASubStatement() {
none()
}
override Expr getASubExpression() {
result = this.getValue()
}
override Expr getASubExpression() { result = this.getValue() }
}
/** A try statement */
class Try extends Try_ {
/* syntax: try: ... */
override Expr getASubExpression() {
none()
}
override Expr getASubExpression() { none() }
override Stmt getASubStatement() {
result = this.getAHandler() or
@@ -486,14 +345,10 @@ class Try extends Try_ {
result = this.getAnOrelse()
}
override ExceptStmt getHandler(int i) {
result = Try_.super.getHandler(i)
}
override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) }
/** Gets an exception handler of this try statement. */
override ExceptStmt getAHandler() {
result = Try_.super.getAHandler()
}
override ExceptStmt getAHandler() { result = Try_.super.getAHandler() }
override Stmt getLastStatement() {
result = this.getFinalbody().getLastItem().getLastStatement()
@@ -501,23 +356,21 @@ class Try extends Try_ {
not exists(this.getFinalbody()) and
result = this.getOrelse().getLastItem().getLastStatement()
or
not exists(this.getFinalbody()) and not exists(this.getOrelse()) and
not exists(this.getFinalbody()) and
not exists(this.getOrelse()) and
result = this.getHandlers().getLastItem().getLastStatement()
or
not exists(this.getFinalbody()) and not exists(this.getOrelse()) and not exists(this.getHandlers()) and
not exists(this.getFinalbody()) and
not exists(this.getOrelse()) and
not exists(this.getHandlers()) and
result = this.getBody().getLastItem().getLastStatement()
}
}
/** A while statement, such as `while parrot_resting():` */
class While extends While_ {
/* syntax: while Expr: ... */
override Expr getASubExpression() {
result = this.getTest()
}
override Expr getASubExpression() { result = this.getTest() }
override Stmt getASubStatement() {
result = this.getAStmt() or
@@ -530,75 +383,44 @@ class While extends While_ {
not exists(this.getOrelse()) and
result = this.getBody().getLastItem().getLastStatement()
}
}
/** A with statement such as `with f as open("file"): text = f.read()` */
class With extends With_ {
/* syntax: with Expr as varname: ... */
override Expr getASubExpression() {
result = this.getContextExpr() or
result = this.getOptionalVars()
}
override Stmt getASubStatement() {
result = this.getAStmt()
}
override Stmt getLastStatement() {
result = this.getBody().getLastItem().getLastStatement()
}
override Stmt getASubStatement() { result = this.getAStmt() }
override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() }
}
/** A plain text used in a template is wrapped in a TemplateWrite statement */
class TemplateWrite extends TemplateWrite_ {
override Expr getASubExpression() { result = this.getValue() }
override Expr getASubExpression() {
result = this.getValue()
}
override Stmt getASubStatement() {
none()
}
override Stmt getASubStatement() { none() }
}
class AsyncFor extends For {
/* syntax: async for varname in Expr: ... */
AsyncFor() {
this.isAsync()
}
AsyncFor() { this.isAsync() }
}
class AsyncWith extends With {
/* syntax: async with Expr as varname: ... */
AsyncWith() {
this.isAsync()
}
AsyncWith() { this.isAsync() }
}
/** A list of statements */
class StmtList extends StmtList_ {
/** Whether this list of statements contains s */
predicate contains(AstNode a) {
exists(Stmt item |
item = this.getAnItem() |
item = a or item.contains(a)
)
exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a))
}
Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) }
}

View File

@@ -1,32 +1,29 @@
/* This file contains test-related utility functions */
import python
/** Removes everything up to the occurrence of `sub` in the string `str` */
bindingset[str,sub]
bindingset[str, sub]
string remove_prefix_before_substring(string str, string sub) {
exists(int index |
index = str.indexOf(sub) and
result = str.suffix(index)
)
or
not exists(str.indexOf(sub)) and
result = str
exists(int index |
index = str.indexOf(sub) and
result = str.suffix(index)
)
or
not exists(str.indexOf(sub)) and
result = str
}
/** Removes the part of the `resources/lib` Python library path that may vary
* from machine to machine. */
/**
* Removes the part of the `resources/lib` Python library path that may vary
* from machine to machine.
*/
string remove_library_prefix(Location loc) {
result = remove_prefix_before_substring(loc.toString(), "resources/lib")
result = remove_prefix_before_substring(loc.toString(), "resources/lib")
}
/** Returns the location of an AST node in compact form: `basename:line:column` */
string compact_location(AstNode a) {
exists(Location l |
l = a.getLocation() |
exists(Location l | l = a.getLocation() |
result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn()
)
}
}

View File

@@ -1,24 +1,19 @@
import python
/** A variable, either a global or local variable (including parameters) */
class Variable extends @py_variable {
Variable() {
exists(string name |
variable(this, _, name) and
not name = "*" and not name = "$"
not name = "*" and
not name = "$"
)
}
/** Gets the identifier (name) of this variable */
string getId() {
variable(this, _, result)
}
string getId() { variable(this, _, result) }
string toString() {
result = "Variable " + this.getId()
}
string toString() { result = "Variable " + this.getId() }
/** Gets an access (load or store) of this variable */
Name getAnAccess() {
@@ -28,103 +23,70 @@ class Variable extends @py_variable {
}
/** Gets a load of this variable */
Name getALoad() {
result.uses(this)
}
Name getALoad() { result.uses(this) }
/** Gets a store of this variable */
Name getAStore() {
result.defines(this)
}
Name getAStore() { result.defines(this) }
/** Gets a use of this variable */
NameNode getAUse() {
result.uses(this)
}
NameNode getAUse() { result.uses(this) }
/** Gets the scope of this variable */
Scope getScope() {
variable(this, result, _)
}
Scope getScope() { variable(this, result, _) }
/** Whether there is an access to this variable outside
/**
* Whether there is an access to this variable outside
* of its own scope. Usually occurs in nested functions
* or for global variables.
*/
predicate escapes() {
exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope())
}
predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) }
/** Whether this variable is a parameter */
predicate isParameter() {
none()
}
predicate isSelf() {
none()
}
predicate isParameter() { none() }
predicate isSelf() { none() }
}
/** A local (function or class) variable */
class LocalVariable extends Variable {
LocalVariable() {
exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class)
}
override string toString() {
result = "Local Variable " + this.getId()
}
override string toString() { result = "Local Variable " + this.getId() }
/** Whether this variable is a parameter */
override predicate isParameter() {
exists(Parameter p | this.getAnAccess() = p)
}
override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) }
/** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */
override predicate isSelf() {
exists(Function f, Parameter self |
exists(Function f, Parameter self |
this.getAnAccess() = self and
f.isMethod() and f.getArg(0) = self
f.isMethod() and
f.getArg(0) = self
)
}
}
/** A local variable that uses "load fast" semantics, for lookup:
* If the variable is undefined, then raise an exception.
/**
* A local variable that uses "load fast" semantics, for lookup:
* If the variable is undefined, then raise an exception.
*/
class FastLocalVariable extends LocalVariable {
FastLocalVariable() {
this.getScope() instanceof FastLocalsFunction
}
FastLocalVariable() { this.getScope() instanceof FastLocalsFunction }
}
/** A local variable that uses "load name" semantics, for lookup:
/**
* A local variable that uses "load name" semantics, for lookup:
* If the variable is undefined, then lookup the value in globals().
*/
class NameLocalVariable extends LocalVariable {
NameLocalVariable() {
not this instanceof FastLocalVariable
}
NameLocalVariable() { not this instanceof FastLocalVariable }
}
/** A global (module-level) variable */
class GlobalVariable extends Variable {
GlobalVariable() { exists(Module m | m = this.getScope()) }
GlobalVariable() {
exists(Module m | m = this.getScope())
}
override string toString() {
result = "Global Variable " + this.getId()
}
override string toString() { result = "Global Variable " + this.getId() }
}