mirror of
https://github.com/github/codeql.git
synced 2026-05-02 12:15:17 +02:00
Python: Autoformat semmle/python top-level.
This commit is contained in:
@@ -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
@@ -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) }
|
||||
}
|
||||
|
||||
|
||||
@@ -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:.*") }
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_ { }
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 & 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()
|
||||
)
|
||||
|
||||
@@ -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)))) }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user