Python: Autoformat all .ql files.

This commit is contained in:
Taus Brock-Nannestad
2020-03-30 11:59:10 +02:00
parent b4fbfa029e
commit 87a9f51c78
491 changed files with 2649 additions and 2747 deletions

View File

@@ -6,7 +6,7 @@
* builtin
* object
*/
import python
from Expr e, string name

View File

@@ -5,7 +5,7 @@
* @tags call
* function
*/
import python
from Value len, CallNode call

View File

@@ -6,11 +6,11 @@
* try
* exception
*/
import python
from ExceptStmt ex, ClassValue cls
where
where
cls.getName() = "MyExceptionClass" and
ex.getType().pointsTo(cls)
select ex

View File

@@ -11,7 +11,8 @@
import python
from IfExp e, ClassObject cls1, ClassObject cls2
where
e.getBody().refersTo(_, cls1, _) and e.getOrelse().refersTo(_, cls2, _) and
where
e.getBody().refersTo(_, cls1, _) and
e.getOrelse().refersTo(_, cls2, _) and
cls1 != cls2
select e
select e

View File

@@ -6,7 +6,7 @@
* block
* statement
*/
import python
from StmtList blk

View File

@@ -1,7 +1,7 @@
/**
* @id py/examples/emptythen
* @name If statements with empty then branch
* @description Finds 'if' statements where the "then" branch
* @description Finds 'if' statements where the "then" branch
* consists entirely of Pass statements
* @tags if
* then
@@ -9,13 +9,13 @@
* conditional
* branch
*/
import python
from If i
where
not exists(Stmt s |
i.getStmt(_) = s and
i.getStmt(_) = s and
not s instanceof Pass
)
select i

View File

@@ -6,7 +6,7 @@
* test
* boolean
*/
import python
from Compare eq

View File

@@ -6,7 +6,7 @@
* equality
* expression statement
*/
import python
from ExprStmt e, Compare eq

View File

@@ -9,11 +9,11 @@
* subtype
* supertype
*/
import python
from ClassObject sub, ClassObject base
where
where
base.getName() = "MyClass" and
sub.getABaseType() = base
select sub

View File

@@ -4,7 +4,7 @@
* @description Finds files called `spam.py`
* @tags file
*/
import python
from File f

View File

@@ -10,4 +10,4 @@ import python
from Function f
where f.isGenerator()
select f
select f

View File

@@ -5,7 +5,7 @@
* @tags integer
* literal
*/
import python
from IntegerLiteral literal

View File

@@ -5,11 +5,11 @@
* @tags call
* method
*/
import python
from AstNode call, PythonFunctionValue method
where
where
method.getQualifiedName() = "MyClass.methodName" and
method.getACall().getNode() = call
select call

View File

@@ -6,7 +6,7 @@
* constructor
* new
*/
import python
from Call new, ClassValue cls

View File

@@ -5,7 +5,7 @@
* @tags method
* override
*/
import python
from FunctionObject override, FunctionObject base

View File

@@ -4,11 +4,11 @@
* @description Find print statements or calls to the builtin function 'print'
* @tags print
*/
import python
from AstNode print
where
where
/* Python 2 without `from __future__ import print_function` */
print instanceof Print
or

View File

@@ -1,19 +1,20 @@
/**
* @id py/examples/private-access
* @name Private access
* @description Find accesses to "private" attributes (those starting with an underscore)
* @description Find accesses to "private" attributes (those starting with an underscore)
* @tags access
* private
*/
import python
predicate is_private(Attribute a) {
a.getName().matches("\\_%") and
a.getName().matches("\\_%") and
not a.getName().matches("\\_\\_%\\_\\_")
}
from Attribute access
where is_private(access) and
not access.getObject().(Name).getId() = "self"
where
is_private(access) and
not access.getObject().(Name).getId() = "self"
select access

View File

@@ -6,13 +6,11 @@
* raise
* exception
*/
import python
from Raise raise, ClassValue ex
where
ex.getName() = "AnException" and
(
raise.getException().pointsTo(ex.getASuperType())
)
raise.getException().pointsTo(ex.getASuperType())
select raise, "Don't raise instances of 'AnException'"

View File

@@ -1,11 +1,11 @@
/**
* @id py/examples/raw-string
* @name Raw string literals
* @description Finds string literals with an 'r' prefix
* @description Finds string literals with an 'r' prefix
* @tags string
* raw
*/
import python
from StrConst s

View File

@@ -8,11 +8,11 @@
* collection
* add
*/
import python
from SubscriptNode store
where
where
store.isStore() and
store.getIndex().pointsTo(Value::named("None"))
select store

View File

@@ -10,6 +10,7 @@
import python
from Try t
where exists(t.getFinalbody())
and not exists(t.getAHandler())
where
exists(t.getFinalbody()) and
not exists(t.getAHandler())
select t

View File

@@ -15,13 +15,13 @@ import python
predicate does_nothing(PyFunctionObject f) {
not exists(Stmt s | s.getScope() = f.getFunction() |
not s instanceof Pass and not ((ExprStmt)s).getValue() = f.getFunction().getDocString()
not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString()
)
}
/* If a method performs a super() call then it is OK as the 'overridden' method will get called */
predicate calls_super(FunctionObject f) {
exists(Call sup, Call meth, Attribute attr, GlobalVariable v |
exists(Call sup, Call meth, Attribute attr, GlobalVariable v |
meth.getScope() = f.getFunction() and
meth.getFunc() = attr and
attr.getObject() = sup and
@@ -33,25 +33,29 @@ predicate calls_super(FunctionObject f) {
/** Holds if the given name is white-listed for some reason */
predicate whitelisted(string name) {
/* The standard library specifically recommends this :(
* See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins */
/*
* The standard library specifically recommends this :(
* See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins
*/
name = "process_request"
}
from ClassObject c, ClassObject b1, ClassObject b2, string name,
int i1, int i2, Object o1, Object o2
where c.getBaseType(i1) = b1 and
c.getBaseType(i2) = b2 and
i1 < i2 and o1 != o2 and
o1 = b1.lookupAttribute(name) and
o2 = b2.lookupAttribute(name) and
not name.matches("\\_\\_%\\_\\_") and
not calls_super(o1) and
not does_nothing(o2) and
not whitelisted(name) and
not o1.overrides(o2) and
not o2.overrides(o1) and
not c.declaresAttribute(name)
select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1, o1.toString(), o2, o2.toString()
from
ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2
where
c.getBaseType(i1) = b1 and
c.getBaseType(i2) = b2 and
i1 < i2 and
o1 != o2 and
o1 = b1.lookupAttribute(name) and
o2 = b2.lookupAttribute(name) and
not name.matches("\\_\\_%\\_\\_") and
not calls_super(o1) and
not does_nothing(o2) and
not whitelisted(name) and
not o1.overrides(o2) and
not o2.overrides(o1) and
not c.declaresAttribute(name)
select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1,
o1.toString(), o2, o2.toString()

View File

@@ -15,7 +15,9 @@ import semmle.python.SelfAttribute
import Equality
predicate class_stores_to_attribute(ClassObject cls, SelfAttributeStore store, string name) {
exists(FunctionObject f | f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name) and
exists(FunctionObject f |
f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name
) and
/* Exclude classes used as metaclasses */
not cls.getASuperType() = theTypeType()
}
@@ -30,23 +32,26 @@ predicate should_override_eq(ClassObject cls, Object base_eq) {
)
}
/** Does the non-overridden __eq__ method access the attribute,
/**
* Does the non-overridden __eq__ method access the attribute,
* which implies that the __eq__ method does not need to be overridden.
*/
predicate superclassEqExpectsAttribute(ClassObject cls, PyFunctionObject base_eq, string attrname) {
not cls.declaresAttribute("__eq__") and
exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
exists(SelfAttributeRead store |
store.getName() = attrname |
exists(SelfAttributeRead store | store.getName() = attrname |
store.getScope() = base_eq.getFunction()
)
)
}
from ClassObject cls, SelfAttributeStore store, Object base_eq
where class_stores_to_attribute(cls, store, _) and should_override_eq(cls, base_eq) and
/* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */
not cls.getASuperType().getName() = "TestCase" and
not superclassEqExpectsAttribute(cls, base_eq, store.getName())
select cls, "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, "'__eq__'", store, store.getName()
where
class_stores_to_attribute(cls, store, _) and
should_override_eq(cls, base_eq) and
/* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */
not cls.getASuperType().getName() = "TestCase" and
not superclassEqExpectsAttribute(cls, base_eq, store.getName())
select cls,
"The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq,
"'__eq__'", store, store.getName()

View File

@@ -14,8 +14,11 @@
import python
CallableValue defines_equality(ClassValue c, string name) {
(name = "__eq__" or major_version() = 2 and name = "__cmp__")
and
(
name = "__eq__"
or
major_version() = 2 and name = "__cmp__"
) and
result = c.declaredAttribute(name)
}
@@ -26,8 +29,12 @@ CallableValue implemented_method(ClassValue c, string name) {
}
string unimplemented_method(ClassValue c) {
not exists(defines_equality(c, _)) and
(result = "__eq__" and major_version() = 3 or major_version() = 2 and result = "__eq__ or __cmp__")
not exists(defines_equality(c, _)) and
(
result = "__eq__" and major_version() = 3
or
major_version() = 2 and result = "__eq__ or __cmp__"
)
or
/* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */
not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2
@@ -41,13 +48,15 @@ predicate unhashable(ClassValue cls) {
}
predicate violates_hash_contract(ClassValue c, string present, string missing, Value method) {
not unhashable(c) and
missing = unimplemented_method(c) and
method = implemented_method(c, present) and
not c.failedInference(_)
not unhashable(c) and
missing = unimplemented_method(c) and
method = implemented_method(c, present) and
not c.failedInference(_)
}
from ClassValue c, string present, string missing, CallableValue method
where violates_hash_contract(c, present, missing, method) and
exists(c.getScope()) // Suppress results that aren't from source
select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, c.getName()
where
violates_hash_contract(c, present, missing, method) and
exists(c.getScope()) // Suppress results that aren't from source
select method, "Class $@ implements " + present + " but does not define " + missing + ".", c,
c.getName()

View File

@@ -13,16 +13,12 @@
import python
import Equality
string equals_or_ne() {
result = "__eq__" or result = "__ne__"
}
string equals_or_ne() { result = "__eq__" or result = "__ne__" }
predicate total_ordering(Class cls) {
exists(Attribute a | a = cls.getADecorator() |
a.getName() = "total_ordering")
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
or
exists(Name n | n = cls.getADecorator() |
n.getId() = "total_ordering")
exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering")
}
CallableValue implemented_method(ClassValue c, string name) {
@@ -33,18 +29,20 @@ string unimplemented_method(ClassValue c) {
not c.declaresAttribute(result) and result = equals_or_ne()
}
predicate violates_equality_contract(ClassValue c, string present, string missing, CallableValue method) {
missing = unimplemented_method(c) and
method = implemented_method(c, present) and
not c.failedInference(_) and
not total_ordering(c.getScope()) and
/* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */
not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and
not method.getScope() instanceof DelegatingEqualityMethod and
not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod
predicate violates_equality_contract(
ClassValue c, string present, string missing, CallableValue method
) {
missing = unimplemented_method(c) and
method = implemented_method(c, present) and
not c.failedInference(_) and
not total_ordering(c.getScope()) and
/* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */
not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and
not method.getScope() instanceof DelegatingEqualityMethod and
not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod
}
from ClassValue c, string present, string missing, CallableValue method
where violates_equality_contract(c, present, missing, method)
select method, "Class $@ implements " + present + " but does not implement " + missing + ".", c, c.getName()
select method, "Class $@ implements " + present + " but does not implement " + missing + ".", c,
c.getName()

View File

@@ -13,17 +13,18 @@
import python
predicate total_ordering(Class cls) {
exists(Attribute a | a = cls.getADecorator() |
a.getName() = "total_ordering")
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
or
exists(Name n | n = cls.getADecorator() |
n.getId() = "total_ordering")
exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering")
}
string ordering_name(int n) {
result = "__lt__" and n = 1 or
result = "__le__" and n = 2 or
result = "__gt__" and n = 3 or
result = "__lt__" and n = 1
or
result = "__le__" and n = 2
or
result = "__gt__" and n = 3
or
result = "__ge__" and n = 4
}
@@ -32,8 +33,7 @@ predicate overrides_ordering_method(ClassValue c, string name) {
(
c.declaresAttribute(name)
or
exists(ClassValue sup |
sup = c.getASuperType() and not sup = Value::named("object") |
exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") |
sup.declaresAttribute(name)
)
)
@@ -41,15 +41,14 @@ predicate overrides_ordering_method(ClassValue c, string name) {
string unimplemented_ordering(ClassValue c, int n) {
not c = Value::named("object") and
not overrides_ordering_method(c, result) and
not overrides_ordering_method(c, result) and
result = ordering_name(n)
}
string unimplemented_ordering_methods(ClassValue c, int n) {
n = 0 and result = "" and exists(unimplemented_ordering(c, _))
or
exists(string prefix, int nm1 |
n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) |
exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) |
prefix = "" and result = unimplemented_ordering(c, n)
or
result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5
@@ -60,16 +59,15 @@ string unimplemented_ordering_methods(ClassValue c, int n) {
Value ordering_method(ClassValue c, string name) {
/* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */
name = ordering_name(_) and result = c.declaredAttribute(name)
name = ordering_name(_) and result = c.declaredAttribute(name)
}
from ClassValue c, Value ordering, string name
where not c.failedInference(_) and
not total_ordering(c.getScope())
and ordering = ordering_method(c, name) and
exists(unimplemented_ordering(c, _))
select c, "Class " + c.getName() + " implements $@, but does not implement " + unimplemented_ordering_methods(c, 4) + ".",
ordering, name
where
not c.failedInference(_) and
not total_ordering(c.getScope()) and
ordering = ordering_method(c, name) and
exists(unimplemented_ordering(c, _))
select c,
"Class " + c.getName() + " implements $@, but does not implement " +
unimplemented_ordering_methods(c, 4) + ".", ordering, name

View File

@@ -13,15 +13,18 @@
import python
ClassObject left_base(ClassObject type, ClassObject base) {
exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i-1))
exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1))
}
predicate invalid_mro(ClassObject t, ClassObject left, ClassObject right) {
t.isNewStyle() and
left = left_base(t, right) and left = right.getAnImproperSuperType()
left = left_base(t, right) and
left = right.getAnImproperSuperType()
}
from ClassObject t, ClassObject left, ClassObject right
where invalid_mro(t, left, right)
select t, "Construction of class " + t.getName() + " can fail due to invalid method resolution order(MRO) for bases $@ and $@.",
left, left.getName(), right, right.getName()
select t,
"Construction of class " + t.getName() +
" can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left,
left.getName(), right, right.getName()

View File

@@ -13,23 +13,18 @@
import python
from ClassObject supercls, string method, Call call,
FunctionObject overriding, FunctionObject overridden
from
ClassObject supercls, string method, Call call, FunctionObject overriding,
FunctionObject overridden
where
exists(FunctionObject init, SelfAttribute sa |
supercls.declaredAttribute("__init__") = init and
call.getScope() = init.getFunction() and call.getFunc() = sa |
sa.getName() = method and
overridden = supercls.declaredAttribute(method) and
overriding.overrides(overridden)
)
select call, "Call to self.$@ in __init__ method, which is overridden by $@.",
overridden, method,
overriding, overriding.descriptiveString()
exists(FunctionObject init, SelfAttribute sa |
supercls.declaredAttribute("__init__") = init and
call.getScope() = init.getFunction() and
call.getFunc() = sa
|
sa.getName() = method and
overridden = supercls.declaredAttribute(method) and
overriding.overrides(overridden)
)
select call, "Call to self.$@ in __init__ method, which is overridden by $@.", overridden, method,
overriding, overriding.descriptiveString()

View File

@@ -16,26 +16,29 @@ import ClassAttributes
predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) {
c.sometimesDefines(a.getName()) and
exists(SelfAttributeRead guard, If i |
exists(SelfAttributeRead guard, If i |
i.contains(a) and
c.assignedInInit(guard.getName()) |
c.assignedInInit(guard.getName())
|
i.getTest() = guard
or
i.getTest().contains(guard)
)
}
predicate maybe_undefined_class_attribute(SelfAttributeRead a, CheckClass c) {
c.sometimesDefines(a.getName()) and
not c.alwaysDefines(a.getName()) and
c.interestingUndefined(a) and
c.interestingUndefined(a) and
not guarded_by_other_attribute(a, c)
}
from Attribute a, ClassObject c, SelfAttributeStore sa
where maybe_undefined_class_attribute(a, c) and
sa.getClass() = c.getPyClass() and sa.getName() = a.getName()
select a, "Attribute '" + a.getName() +
"' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, "here"
where
maybe_undefined_class_attribute(a, c) and
sa.getClass() = c.getPyClass() and
sa.getName() = a.getName()
select a,
"Attribute '" + a.getName() +
"' is not defined in the class body nor in the __init__() method, but it is defined $@", sa,
"here"

View File

@@ -13,14 +13,12 @@
import python
import MethodCallOrder
from ClassObject self, FunctionObject missing
where
missing_call_to_superclass_method(self, _, missing, "__del__") and
not missing.neverReturns() and
not self.failedInference() and
not missing.isBuiltin()
select self, "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.",
missing, missing.descriptiveString()
select self,
"Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.",
missing, missing.descriptiveString()

View File

@@ -14,7 +14,6 @@ import python
import MethodCallOrder
from ClassObject self, FunctionObject initializer, FunctionObject missing
where
self.lookupAttribute("__init__") = initializer and
missing_call_to_superclass_method(self, initializer, missing, "__init__") and
@@ -24,5 +23,6 @@ where
not self.failedInference() and
not missing.isBuiltin() and
not self.isAbstract()
select self, "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.",
missing, missing.descriptiveString(), initializer, "__init__ method"
select self,
"Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.",
missing, missing.descriptiveString(), initializer, "__init__ method"

View File

@@ -15,8 +15,7 @@ import python
predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) {
cls.isDescriptorType() and
exists(PyFunctionObject f, PyFunctionObject get_set |
exists(string name |
cls.lookupAttribute(name) = get_set |
exists(string name | cls.lookupAttribute(name) = get_set |
name = "__get__" or name = "__set__" or name = "__delete__"
) and
cls.lookupAttribute(_) = f and
@@ -27,7 +26,7 @@ predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) {
}
from ClassObject cls, SelfAttributeStore s
where
mutates_descriptor(cls, s)
select s, "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", cls, cls.getName()
where mutates_descriptor(cls, s)
select s,
"Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.",
cls, cls.getName()

View File

@@ -13,59 +13,80 @@
import python
class InitCallStmt extends ExprStmt {
InitCallStmt() {
exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() |
attr.getName() = "__init__")
attr.getName() = "__init__"
)
}
}
predicate overwrites_which(Function subinit, AssignStmt write_attr, string which) {
write_attr.getScope() = subinit and self_write_stmt(write_attr, _) and
write_attr.getScope() = subinit and
self_write_stmt(write_attr, _) and
exists(Stmt top | top.contains(write_attr) or top = write_attr |
(exists(int i, int j, InitCallStmt call | call.getScope() = subinit | i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass")
or
exists(int i, int j, InitCallStmt call | call.getScope() = subinit | i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass")
)
)
(
exists(int i, int j, InitCallStmt call | call.getScope() = subinit |
i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass"
)
or
exists(int i, int j, InitCallStmt call | call.getScope() = subinit |
i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass"
)
)
)
}
predicate self_write_stmt(Stmt s, string attr) {
exists(Attribute a, Name self | self = a.getObject() and s.contains(a) and self.getId() = "self" and a.getCtx() instanceof Store and a.getName() = attr)
exists(Attribute a, Name self |
self = a.getObject() and
s.contains(a) and
self.getId() = "self" and
a.getCtx() instanceof Store and
a.getName() = attr
)
}
predicate both_assign_attribute(Stmt s1, Stmt s2, Function f1, Function f2) {
exists(string name | s1.getScope() = f1 and s2.getScope() = f2 and self_write_stmt(s1, name) and self_write_stmt(s2, name))
exists(string name |
s1.getScope() = f1 and
s2.getScope() = f2 and
self_write_stmt(s1, name) and
self_write_stmt(s2, name)
)
}
predicate attribute_overwritten(AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname)
{
exists(FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, AssignStmt subattr, AssignStmt superattr |
(classtype = "superclass" and classname = superclass.getName() and overwrites = subattr and overwritten = superattr or
classtype = "subclass" and classname = subclass.getName() and overwrites = superattr and overwritten = subattr)
and
/* OK if overwritten in subclass and is a class attribute */
(not exists(superclass.declaredAttribute(name)) or classtype = "subclass")
and
superclass.declaredAttribute("__init__") = superinit
and
subclass.declaredAttribute("__init__") = subinit
and
superclass = subclass.getASuperType()
and
overwrites_which(subinit.getFunction(), subattr, classtype)
and
both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction())
and
self_write_stmt(superattr, name)
)
predicate attribute_overwritten(
AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname
) {
exists(
FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass,
AssignStmt subattr, AssignStmt superattr
|
(
classtype = "superclass" and
classname = superclass.getName() and
overwrites = subattr and
overwritten = superattr
or
classtype = "subclass" and
classname = subclass.getName() and
overwrites = superattr and
overwritten = subattr
) and
/* OK if overwritten in subclass and is a class attribute */
(not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and
superclass.declaredAttribute("__init__") = superinit and
subclass.declaredAttribute("__init__") = subinit and
superclass = subclass.getASuperType() and
overwrites_which(subinit.getFunction(), subattr, classtype) and
both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and
self_write_stmt(superattr, name)
)
}
from string classtype, AssignStmt overwrites, AssignStmt overwritten, string name, string classname
where attribute_overwritten(overwrites, overwritten, name, classtype, classname)
select overwrites, "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + " $@.", overwritten, classname
select overwrites,
"Assignment overwrites attribute " + name + ", which was previously defined in " + classtype +
" $@.", overwritten, classname

View File

@@ -14,4 +14,6 @@ import python
from PropertyObject prop, ClassObject cls
where cls.declaredAttribute(_) = prop and not cls.failedInference() and not cls.isNewStyle()
select prop, "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + " is an old-style class."
select prop,
"Property " + prop.getName() + " will not work properly, as class " + cls.getName() +
" is an old-style class."

View File

@@ -16,4 +16,6 @@ import python
from ClassObject c
where not c.isC() and not c.isContextManager() and exists(c.declaredAttribute("__del__"))
select c, "Class " + c.getName() + " implements __del__ (presumably to release some resource). Consider making it a context manager."
select c,
"Class " + c.getName() +
" implements __del__ (presumably to release some resource). Consider making it a context manager."

View File

@@ -11,30 +11,36 @@
* @id py/attribute-shadows-method
*/
/* Determine if a class defines a method that is shadowed by an attribute
defined in a super-class
*/
/*
* Determine if a class defines a method that is shadowed by an attribute
* defined in a super-class
*/
/* Need to find attributes defined in superclass (only in __init__?) */
import python
predicate shadowed_by_super_class(ClassObject c, ClassObject supercls, Assign assign, FunctionObject f)
{
c.getASuperType() = supercls and c.declaredAttribute(_) = f and
predicate shadowed_by_super_class(
ClassObject c, ClassObject supercls, Assign assign, FunctionObject f
) {
c.getASuperType() = supercls and
c.declaredAttribute(_) = f and
exists(FunctionObject init, Attribute attr |
supercls.declaredAttribute("__init__") = init and
attr = assign.getATarget() and
((Name)attr.getObject()).getId() = "self" and
attr.getObject().(Name).getId() = "self" and
attr.getName() = f.getName() and
assign.getScope() = ((FunctionExpr)init.getOrigin()).getInnerScope()
assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope()
) and
/* It's OK if the super class defines the method as well.
* We assume that the original method must have been defined for a reason. */
/*
* It's OK if the super class defines the method as well.
* We assume that the original method must have been defined for a reason.
*/
not supercls.hasAttribute(f.getName())
}
from ClassObject c, ClassObject supercls, Assign assign, FunctionObject shadowed
where shadowed_by_super_class(c, supercls, assign, shadowed)
select shadowed.getOrigin(), "Method " + shadowed.getName() + " is shadowed by $@ in super class '"+ supercls.getName() + "'.", assign, "an attribute"
select shadowed.getOrigin(),
"Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.",
assign, "an attribute"

View File

@@ -13,10 +13,15 @@
import python
predicate uses_of_super_in_old_style_class(Call s) {
exists(Function f, ClassObject c | s.getScope() = f and f.getScope() = c.getPyClass() and not c.failedInference() and
not c.isNewStyle() and ((Name)s.getFunc()).getId() = "super")
exists(Function f, ClassObject c |
s.getScope() = f and
f.getScope() = c.getPyClass() and
not c.failedInference() and
not c.isNewStyle() and
s.getFunc().(Name).getId() = "super"
)
}
from Call c
where uses_of_super_in_old_style_class(c)
select c, "super() will not work in old-style classes"
select c, "super() will not work in old-style classes"

View File

@@ -13,15 +13,16 @@
import python
import MethodCallOrder
from ClassObject self, FunctionObject multi
where
multiple_calls_to_superclass_method(self, multi, "__del__") and
not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and
not exists(FunctionObject better |
multiple_calls_to_superclass_method(self, better, "__del__") and
better.overrides(multi)
) and
not self.failedInference()
select self, "Class " + self.getName() + " may not be cleaned up properly as $@ may be called multiple times during destruction.",
multi, multi.descriptiveString()
where
multiple_calls_to_superclass_method(self, multi, "__del__") and
not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and
not exists(FunctionObject better |
multiple_calls_to_superclass_method(self, better, "__del__") and
better.overrides(multi)
) and
not self.failedInference()
select self,
"Class " + self.getName() +
" may not be cleaned up properly as $@ may be called multiple times during destruction.", multi,
multi.descriptiveString()

View File

@@ -14,13 +14,16 @@ import python
import MethodCallOrder
from ClassObject self, FunctionObject multi
where multi != theObjectType().lookupAttribute("__init__") and
multiple_calls_to_superclass_method(self, multi, "__init__") and
not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and
not exists(FunctionObject better |
multiple_calls_to_superclass_method(self, better, "__init__") and
better.overrides(multi)
) and
not self.failedInference()
select self, "Class " + self.getName() + " may not be initialized properly as $@ may be called multiple times during initialization.",
multi, multi.descriptiveString()
where
multi != theObjectType().lookupAttribute("__init__") and
multiple_calls_to_superclass_method(self, multi, "__init__") and
not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and
not exists(FunctionObject better |
multiple_calls_to_superclass_method(self, better, "__init__") and
better.overrides(multi)
) and
not self.failedInference()
select self,
"Class " + self.getName() +
" may not be initialized properly as $@ may be called multiple times during initialization.",
multi, multi.descriptiveString()

View File

@@ -32,4 +32,3 @@ predicate report_undefined_class_attribute(Attribute a, ClassObject c, string na
from Attribute a, ClassObject c, string name
where report_undefined_class_attribute(a, c, name)
select a, "Attribute '" + name + "' is not defined in either the class body or in any method"

View File

@@ -21,63 +21,68 @@ predicate does_not_define_special_method(Class cls) {
not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod())
}
predicate no_inheritance(Class c) {
not exists(ClassObject cls, ClassObject other |
cls.getPyClass() = c and
other != theObjectType() |
cls.getPyClass() = c and
other != theObjectType()
|
other.getABaseType() = cls or
cls.getABaseType() = other
)
and
) and
not exists(Expr base | base = c.getABase() |
not base instanceof Name or ((Name)base).getId() != "object"
not base instanceof Name or base.(Name).getId() != "object"
)
}
predicate is_decorated(Class c) {
exists(c.getADecorator())
}
predicate is_decorated(Class c) { exists(c.getADecorator()) }
predicate is_stateful(Class c) {
exists(Function method, ExprContext ctx |
method.getScope() = c and (ctx instanceof Store or ctx instanceof AugStore) |
exists(Function method, ExprContext ctx |
method.getScope() = c and
(ctx instanceof Store or ctx instanceof AugStore)
|
exists(Subscript s | s.getScope() = method and s.getCtx() = ctx)
or
exists(Attribute a | a.getScope() = method and a.getCtx() = ctx)
)
or
exists(Function method, Call call, Attribute a, string name |
method.getScope() = c and call.getScope() = method and
call.getFunc() = a and a.getName() = name |
name = "pop" or name = "remove" or name = "discard" or
name = "extend" or name = "append"
exists(Function method, Call call, Attribute a, string name |
method.getScope() = c and
call.getScope() = method and
call.getFunc() = a and
a.getName() = name
|
name = "pop" or
name = "remove" or
name = "discard" or
name = "extend" or
name = "append"
)
}
predicate useless_class(Class c, int methods) {
c.isTopLevel()
and
c.isPublic()
and
no_inheritance(c)
and
fewer_than_two_public_methods(c, methods)
and
does_not_define_special_method(c)
and
not c.isProbableMixin()
and
not is_decorated(c)
and
c.isTopLevel() and
c.isPublic() and
no_inheritance(c) and
fewer_than_two_public_methods(c, methods) and
does_not_define_special_method(c) and
not c.isProbableMixin() and
not is_decorated(c) and
not is_stateful(c)
}
from Class c, int methods, string msg
where useless_class(c, methods) and
(methods = 1 and msg = "Class " + c.getName() + " defines only one public method, which should be replaced by a function."
or
methods = 0 and msg = "Class " + c.getName() + " defines no public methods and could be replaced with a namedtuple or dictionary."
)
where
useless_class(c, methods) and
(
methods = 1 and
msg =
"Class " + c.getName() +
" defines only one public method, which should be replaced by a function."
or
methods = 0 and
msg =
"Class " + c.getName() +
" defines no public methods and could be replaced with a namedtuple or dictionary."
)
select c, msg

View File

@@ -14,14 +14,11 @@
*/
import python
import Expressions.CallArgs
from Call call, ClassObject cls, string name, FunctionObject init
where
illegally_named_parameter_objectapi(call, cls, name)
and init = get_function_or_initializer_objectapi(cls)
select
call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, init.getQualifiedName()
illegally_named_parameter_objectapi(call, cls, name) and
init = get_function_or_initializer_objectapi(cls)
select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init,
init.getQualifiedName()

View File

@@ -17,9 +17,15 @@ import Expressions.CallArgs
from Call call, ClassObject cls, string too, string should, int limit, FunctionObject init
where
(
too_many_args_objectapi(call, cls, limit) and too = "too many arguments" and should = "no more than "
or
too_few_args_objectapi(call, cls, limit) and too = "too few arguments" and should = "no fewer than "
) and init = get_function_or_initializer_objectapi(cls)
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, init.getQualifiedName()
(
too_many_args_objectapi(call, cls, limit) and
too = "too many arguments" and
should = "no more than "
or
too_few_args_objectapi(call, cls, limit) and
too = "too few arguments" and
should = "no fewer than "
) and
init = get_function_or_initializer_objectapi(cls)
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init,
init.getQualifiedName()

View File

@@ -14,17 +14,16 @@
import python
predicate doesnt_reraise(ExceptStmt ex) {
ex.getAFlowNode().getBasicBlock().reachesExit()
}
predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() }
predicate catches_base_exception(ExceptStmt ex) {
ex.getType().pointsTo(ClassValue::baseException())
or
not exists(ex.getType())
ex.getType().pointsTo(ClassValue::baseException())
or
not exists(ex.getType())
}
from ExceptStmt ex
where catches_base_exception(ex) and
doesnt_reraise(ex)
where
catches_base_exception(ex) and
doesnt_reraise(ex)
select ex, "Except block directly handles BaseException."

View File

@@ -13,21 +13,18 @@
import python
predicate
empty_except(ExceptStmt ex) {
predicate empty_except(ExceptStmt ex) {
not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass)
}
predicate no_else(ExceptStmt ex) {
not exists(ex.getTry().getOrelse())
}
predicate no_else(ExceptStmt ex) { not exists(ex.getTry().getOrelse()) }
predicate no_comment(ExceptStmt ex) {
not exists(Comment c |
c.getLocation().getFile() = ex.getLocation().getFile() and
c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and
c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine()
)
not exists(Comment c |
c.getLocation().getFile() = ex.getLocation().getFile() and
c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and
c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine()
)
}
predicate non_local_control_flow(ExceptStmt ex) {
@@ -38,7 +35,8 @@ predicate try_has_normal_exit(Try try) {
exists(ControlFlowNode pred, ControlFlowNode succ |
/* Exists a non-exception predecessor, successor pair */
pred.getASuccessor() = succ and
not pred.getAnExceptionalSuccessor() = succ |
not pred.getAnExceptionalSuccessor() = succ
|
/* Successor is either a normal flow node or a fall-through exit */
not exists(Scope s | s.getReturnNode() = succ) and
/* Predecessor is in try body and successor is not */
@@ -50,8 +48,7 @@ predicate try_has_normal_exit(Try try) {
predicate attribute_access(Stmt s) {
s.(ExprStmt).getValue() instanceof Attribute
or
exists(string name |
s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name |
exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name |
name = "getattr" or name = "setattr" or name = "delattr"
)
or
@@ -65,8 +62,7 @@ predicate subscript(Stmt s) {
}
predicate encode_decode(Call ex, ClassValue type) {
exists(string name |
ex.getFunc().(Attribute).getName() = name |
exists(string name | ex.getFunc().(Attribute).getName() = name |
name = "encode" and type = ClassValue::unicodeEncodeError()
or
name = "decode" and type = ClassValue::unicodeDecodeError()
@@ -80,8 +76,7 @@ predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) {
}
predicate focussed_handler(ExceptStmt ex) {
exists(Stmt s, ClassValue type |
small_handler(ex, s, type) |
exists(Stmt s, ClassValue type | small_handler(ex, s, type) |
subscript(s) and type.getASuperType() = ClassValue::lookupError()
or
attribute_access(s) and type = ClassValue::attributeError()
@@ -92,12 +87,15 @@ predicate focussed_handler(ExceptStmt ex) {
)
}
Try try_return() {
not exists(result.getStmt(1)) and result.getStmt(0) instanceof Return
}
Try try_return() { not exists(result.getStmt(1)) and result.getStmt(0) instanceof Return }
from ExceptStmt ex
where empty_except(ex) and no_else(ex) and no_comment(ex) and not non_local_control_flow(ex)
and not ex.getTry() = try_return() and try_has_normal_exit(ex.getTry()) and
not focussed_handler(ex)
where
empty_except(ex) and
no_else(ex) and
no_comment(ex) and
not non_local_control_flow(ex) and
not ex.getTry() = try_return() and
try_has_normal_exit(ex.getTry()) and
not focussed_handler(ex)
select ex, "'except' clause does nothing but pass and there is no explanatory comment."

View File

@@ -16,6 +16,10 @@ import Raising
import Exceptions.NotImplemented
from Raise r, ClassValue t
where type_or_typeof(r, t, _) and not t.isLegalExceptionType() and not t.failedInference(_) and not use_of_not_implemented_in_raise(r, _)
select r, "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead."
where
type_or_typeof(r, t, _) and
not t.isLegalExceptionType() and
not t.failedInference(_) and
not use_of_not_implemented_in_raise(r, _)
select r,
"Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead."

View File

@@ -15,5 +15,4 @@ import Exceptions.NotImplemented
from Expr notimpl
where use_of_not_implemented_in_raise(_, notimpl)
select notimpl, "NotImplemented is not an Exception. Did you mean NotImplementedError?"

View File

@@ -12,8 +12,11 @@
import python
from Raise r, Value v, AstNode origin
where r.getException().pointsTo(v, origin) and
v.getClass() = ClassValue::tuple() and
major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */
select r, "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", origin, "a tuple"
where
r.getException().pointsTo(v, origin) and
v.getClass() = ClassValue::tuple() and
major_version() = 2
/* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */
select r,
"Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.",
origin, "a tuple"

View File

@@ -12,13 +12,9 @@
import python
FunctionValue iter() {
result = Value::named("iter")
}
FunctionValue iter() { result = Value::named("iter") }
BuiltinFunctionValue next() {
result = Value::named("next")
}
BuiltinFunctionValue next() { result = Value::named("next") }
predicate call_to_iter(CallNode call, EssaVariable sequence) {
sequence.getAUse() = iter().getArgumentForCall(call, 0)
@@ -52,14 +48,14 @@ predicate stop_iteration_handled(CallNode call) {
}
from CallNode call
where call_to_next(call, _) and
not call_to_next_has_default(call) and
not exists(EssaVariable iterator |
call_to_next(call, iterator.getAUse()) and
iter_not_exhausted(iterator)
) and
call.getNode().getScope().(Function).isGenerator() and
not exists(Comp comp | comp.contains(call.getNode())) and
not stop_iteration_handled(call)
where
call_to_next(call, _) and
not call_to_next_has_default(call) and
not exists(EssaVariable iterator |
call_to_next(call, iterator.getAUse()) and
iter_not_exhausted(iterator)
) and
call.getNode().getScope().(Function).isGenerator() and
not exists(Comp comp | comp.contains(call.getNode())) and
not stop_iteration_handled(call)
select call, "Call to next() in a generator"

View File

@@ -16,14 +16,14 @@ import python
from CallNode call_to_super, string name
where
exists(GlobalVariable gv, ControlFlowNode cn |
call_to_super = ClassValue::super_().getACall() and
gv.getId() = "super" and
cn = call_to_super.getArg(0) and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(ClassValue other |
cn.pointsTo(other) and
not other.getScope().getName() = name
exists(GlobalVariable gv, ControlFlowNode cn |
call_to_super = ClassValue::super_().getACall() and
gv.getId() = "super" and
cn = call_to_super.getArg(0) and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(ClassValue other |
cn.pointsTo(other) and
not other.getScope().getName() = name
)
)
)
select call_to_super.getNode(), "First argument to super() should be " + name + "."

View File

@@ -16,6 +16,8 @@ import python
from Compare comparison, Expr left, Expr right
where
comparison.compares(left, _, right) and left.isConstant() and right.isConstant() and
comparison.compares(left, _, right) and
left.isConstant() and
right.isConstant() and
not exists(Assert a | a.getTest() = comparison)
select comparison, "Comparison of constants; use 'True' or 'False' instead."

View File

@@ -16,6 +16,5 @@ import python
import Expressions.RedundantComparison
from RedundantComparison comparison
where
comparison.maybeMissingSelf()
where comparison.maybeMissingSelf()
select comparison, "Comparison of identical values; may be missing 'self'."

View File

@@ -15,16 +15,19 @@
import python
import semmle.python.Comparisons
/* Holds if the comparison `comp` is of the complex form `a op b op c` and not of
/*
* Holds if the comparison `comp` is of the complex form `a op b op c` and not of
* the simple form `a op b`.
*/
private predicate is_complex(Expr comp) {
exists(comp.(Compare).getOp(1))
or
is_complex(comp.(UnaryExpr).getOperand())
}
/** A test is useless if for every block that it controls there is another test that is at least as
/**
* A test is useless if for every block that it controls there is another test that is at least as
* strict and also controls that block.
*/
private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) {
@@ -34,17 +37,15 @@ private predicate useless_test(Comparison comp, ComparisonControlBlock controls,
}
private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) {
forex(Comparison compnode, ConditionBlock block|
forex(Comparison compnode, ConditionBlock block |
compnode.getNode() = comp and
block.getLastNode().getNode() = previous
|
|
useless_test(compnode, block, isTrue)
)
}
from Expr test, Expr other, boolean isTrue
where
useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _)
where
useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _)
select test, "Test is always " + isTrue + ", because of $@", other, "this condition"

View File

@@ -17,13 +17,12 @@ import semmle.python.strings
predicate dict_key(Dict d, Expr k, string s) {
k = d.getAKey() and
(
s = ((Num)k).getN()
s = k.(Num).getN()
or
// We use <20> to mark unrepresentable characters
// so two instances of <20> may represent different strings in the source code
not "<22>" = s.charAt(_) and
exists(StrConst c |
c = k |
exists(StrConst c | c = k |
s = "u\"" + c.getText() + "\"" and c.isUnicode()
or
s = "b\"" + c.getText() + "\"" and not c.isUnicode()
@@ -32,13 +31,15 @@ predicate dict_key(Dict d, Expr k, string s) {
}
from Dict d, Expr k1, Expr k2
where exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
(
exists(BasicBlock b, int i1, int i2 |
k1.getAFlowNode() = b.getNode(i1) and
k2.getAFlowNode() = b.getNode(i2) and
i1 < i2
) or
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
)
where
exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
(
exists(BasicBlock b, int i1, int i2 |
k1.getAFlowNode() = b.getNode(i1) and
k2.getAFlowNode() = b.getNode(i2) and
i1 < i2
)
or
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
)
select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten"

View File

@@ -13,23 +13,22 @@
import python
class DelCall extends Call {
DelCall() {
((Attribute)this.getFunc()).getName() = "__del__"
}
predicate isSuperCall() {
exists(Function f | f = this.getScope() and f.getName() = "__del__" |
// We pass in `self` as the first argument...
f.getArg(0).asName().getVariable() = ((Name)this.getArg(0)).getVariable() or
// ... or the call is of the form `super(Type, self).__del__()`, or the equivalent
// Python 3: `super().__del__()`.
exists(Call superCall | superCall = ((Attribute)this.getFunc()).getObject() |
((Name)superCall.getFunc()).getId() = "super"
)
)
}
DelCall() { this.getFunc().(Attribute).getName() = "__del__" }
predicate isSuperCall() {
exists(Function f | f = this.getScope() and f.getName() = "__del__" |
// We pass in `self` as the first argument...
f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable()
or
// ... or the call is of the form `super(Type, self).__del__()`, or the equivalent
// Python 3: `super().__del__()`.
exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() |
superCall.getFunc().(Name).getId() = "super"
)
)
}
}
from DelCall del
where not del.isSuperCall()
select del, "The __del__ special method is called explicitly."
select del, "The __del__ special method is called explicitly."

View File

@@ -15,4 +15,4 @@ import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt
where call.getAFormat() = fmt and fmt.isImplicitlyNumbered() and fmt.isExplicitlyNumbered()
select fmt, "Formatting string mixes implicitly and explicitly numbered fields."
select fmt, "Formatting string mixes implicitly and explicitly numbered fields."

View File

@@ -11,16 +11,18 @@
*/
import python
import python
import AdvancedFormatting
int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _)) + 1 }
from AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field
where arg_count = call.providedArgCount() and max_field = field_count(fmt) and
call.getAFormat() = fmt and not exists(call.getStarargs()) and
forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count)
select call, "Too many arguments for string format. Format $@ requires only " + max_field + ", but " +
arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\""
where
arg_count = call.providedArgCount() and
max_field = field_count(fmt) and
call.getAFormat() = fmt and
not exists(call.getStarargs()) and
forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count)
select call,
"Too many arguments for string format. Format $@ requires only " + max_field + ", but " +
arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\""

View File

@@ -14,14 +14,18 @@ import python
import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt, string name, string fmt_repr
where call.getAFormat() = fmt and
name = call.getAKeyword().getArg() and
forall(AdvancedFormatString format | format = call.getAFormat() | not format.getFieldName(_, _) = name)
and not exists(call.getKwargs()) and
(strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\""
or
strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used."
)
select call, "Surplus named argument for string format. An argument named '" + name +
"' is provided, but it is not required by $@.", fmt, fmt_repr
where
call.getAFormat() = fmt and
name = call.getAKeyword().getArg() and
forall(AdvancedFormatString format | format = call.getAFormat() |
not format.getFieldName(_, _) = name
) and
not exists(call.getKwargs()) and
(
strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\""
or
strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used."
)
select call,
"Surplus named argument for string format. An argument named '" + name +
"' is provided, but it is not required by $@.", fmt, fmt_repr

View File

@@ -15,9 +15,11 @@ import python
import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt, string name
where call.getAFormat() = fmt and
not name = call.getAKeyword().getArg() and
fmt.getFieldName(_, _) = name
and not exists(call.getKwargs())
select call, "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.",
fmt, "\"" + fmt.getText() + "\""
where
call.getAFormat() = fmt and
not name = call.getAKeyword().getArg() and
fmt.getFieldName(_, _) = name and
not exists(call.getKwargs())
select call,
"Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.",
fmt, "\"" + fmt.getText() + "\""

View File

@@ -14,10 +14,16 @@
import python
import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt,
int arg_count, int max_field, string provided
where arg_count = call.providedArgCount() and max_field = max(fmt.getFieldNumber(_, _)) and
call.getAFormat() = fmt and not exists(call.getStarargs()) and arg_count <= max_field and
(if arg_count = 1 then provided = " is provided." else provided = " are provided.")
select call, "Too few arguments for string format. Format $@ requires at least " + (max_field+1) + ", but " +
arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\""
from
AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field,
string provided
where
arg_count = call.providedArgCount() and
max_field = max(fmt.getFieldNumber(_, _)) and
call.getAFormat() = fmt and
not exists(call.getStarargs()) and
arg_count <= max_field and
(if arg_count = 1 then provided = " is provided." else provided = " are provided.")
select call,
"Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " +
arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\""

View File

@@ -12,7 +12,8 @@
import python
/* This assumes that any indexing operation where the value is not a sequence or numpy array involves hashing.
/*
* This assumes that any indexing operation where the value is not a sequence or numpy array involves hashing.
* For sequences, the index must be an int, which are hashable, so we don't need to treat them specially.
* For numpy arrays, the index may be a list, which are not hashable and needs to be treated specially.
*/
@@ -30,7 +31,9 @@ predicate has_custom_getitem(Value v) {
}
predicate explicitly_hashed(ControlFlowNode f) {
exists(CallNode c, GlobalVariable hash | c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash")
exists(CallNode c, GlobalVariable hash |
c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash"
)
}
predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode origin) {
@@ -44,9 +47,7 @@ predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode
}
predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origin) {
exists(Value v |
f.pointsTo(v, origin) and v.getClass() = cls
|
exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls |
not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle()
or
cls.lookup("__hash__") = Value::named("None")
@@ -67,16 +68,18 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi
* it.
*/
predicate typeerror_is_caught(ControlFlowNode f) {
exists (Try try |
exists(Try try |
try.getBody().contains(f.getNode()) and
try.getAHandler().getType().pointsTo(ClassValue::typeError()))
try.getAHandler().getType().pointsTo(ClassValue::typeError())
)
}
from ControlFlowNode f, ClassValue c, ControlFlowNode origin
where
not typeerror_is_caught(f)
and
(explicitly_hashed(f) and is_unhashable(f, c, origin)
or
unhashable_subscript(f, c, origin))
not typeerror_is_caught(f) and
(
explicitly_hashed(f) and is_unhashable(f, c, origin)
or
unhashable_subscript(f, c, origin)
)
select f.getNode(), "This $@ of $@ is unhashable.", origin, "instance", c, c.getQualifiedName()

View File

@@ -14,7 +14,14 @@ import python
import IsComparisons
from Compare comp, Cmpop op, ClassValue c, string alt
where invalid_portable_is_comparison(comp, op, c) and
not cpython_interned_constant(comp.getASubExpression()) and
(op instanceof Is and alt = "==" or op instanceof IsNot and alt = "!=")
select comp, "Values compared using '" + op.getSymbol() + "' when equivalence is not the same as identity. Use '" + alt + "' instead."
where
invalid_portable_is_comparison(comp, op, c) and
not cpython_interned_constant(comp.getASubExpression()) and
(
op instanceof Is and alt = "=="
or
op instanceof IsNot and alt = "!="
)
select comp,
"Values compared using '" + op.getSymbol() +
"' when equivalence is not the same as identity. Use '" + alt + "' instead."

View File

@@ -15,10 +15,13 @@ import python
import Exceptions.NotImplemented
from Call c, Value v, ClassValue t, Expr f, AstNode origin
where f = c.getFunc() and f.pointsTo(v, origin) and t = v.getClass() and
not t.isCallable() and not t.failedInference(_)
and not t.hasAttribute("__get__")
and not v = Value::named("None")
and not use_of_not_implemented_in_raise(_, f)
where
f = c.getFunc() and
f.pointsTo(v, origin) and
t = v.getClass() and
not t.isCallable() and
not t.failedInference(_) and
not t.hasAttribute("__get__") and
not v = Value::named("None") and
not use_of_not_implemented_in_raise(_, f)
select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString()

View File

@@ -14,10 +14,12 @@ import python
import IsComparisons
from Compare comp, Cmpop op, ClassValue c
where invalid_portable_is_comparison(comp, op, c) and
exists(Expr sub |
sub = comp.getASubExpression() |
cpython_interned_constant(sub) and
not universally_interned_constant(sub)
)
select comp, "The result of this comparison with '" + op.getSymbol() + "' may differ between implementations of Python."
where
invalid_portable_is_comparison(comp, op, c) and
exists(Expr sub | sub = comp.getASubExpression() |
cpython_interned_constant(sub) and
not universally_interned_constant(sub)
)
select comp,
"The result of this comparison with '" + op.getSymbol() +
"' may differ between implementations of Python."

View File

@@ -14,9 +14,8 @@ import python
import semmle.python.regex
from Regex r, int offset
where r.escapingChar(offset) and r.getChar(offset+1) = "b" and
exists(int start, int end |
start < offset and end > offset |
r.charSet(start, end)
)
select r, "Backspace escape in regular expression at offset " + offset + "."
where
r.escapingChar(offset) and
r.getChar(offset + 1) = "b" and
exists(int start, int end | start < offset and end > offset | r.charSet(start, end))
select r, "Backspace escape in regular expression at offset " + offset + "."

View File

@@ -15,20 +15,28 @@ import semmle.python.regex
predicate duplicate_char_in_class(Regex r, string char) {
exists(int i, int j, int x, int y, int start, int end |
i != x and j != y and
start < i and j < end and
start < x and y < end and
r.character(i, j) and char = r.getText().substring(i, j) and
r.character(x, y) and char = r.getText().substring(x, y) and
i != x and
j != y and
start < i and
j < end and
start < x and
y < end and
r.character(i, j) and
char = r.getText().substring(i, j) and
r.character(x, y) and
char = r.getText().substring(x, y) and
r.charSet(start, end)
) and
/* Exclude <20> as we use it for any unencodable character */
char != "<22>" and
//Ignore whitespace in verbose mode
not (r.getAMode() = "VERBOSE" and (char = " " or char = "\t" or char = "\r" or char = "\n"))
not (
r.getAMode() = "VERBOSE" and
(char = " " or char = "\t" or char = "\r" or char = "\n")
)
}
from Regex r, string char
where duplicate_char_in_class(r, char)
select r, "This regular expression includes duplicate character '" + char + "' in a set of characters."
select r,
"This regular expression includes duplicate character '" + char + "' in a set of characters."

View File

@@ -16,5 +16,3 @@ import semmle.python.regex
from Regex r, string missing, string part
where r.getText().regexpMatch(".*\\(P<\\w+>.*") and missing = "?" and part = "named group"
select r, "Regular expression is missing '" + missing + "' in " + part + "."

View File

@@ -16,10 +16,11 @@ import semmle.python.regex
predicate unmatchable_caret(Regex r, int start) {
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
r.specialCharacter(start, start+1, "^") and
not r.firstItem(start, start+1)
r.specialCharacter(start, start + 1, "^") and
not r.firstItem(start, start + 1)
}
from Regex r, int offset
where unmatchable_caret(r, offset)
select r, "This regular expression includes an unmatchable caret at offset " + offset.toString() + "."
select r,
"This regular expression includes an unmatchable caret at offset " + offset.toString() + "."

View File

@@ -16,11 +16,11 @@ import semmle.python.regex
predicate unmatchable_dollar(Regex r, int start) {
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
r.specialCharacter(start, start+1, "$")
and
not r.lastItem(start, start+1)
r.specialCharacter(start, start + 1, "$") and
not r.lastItem(start, start + 1)
}
from Regex r, int offset
where unmatchable_dollar(r, offset)
select r, "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "."
select r,
"This regular expression includes an unmatchable dollar at offset " + offset.toString() + "."

View File

@@ -1,38 +1,37 @@
/**
* @name Result of integer division may be truncated
* @description The arguments to a division statement may be integers, which
* may cause the result to be truncated in Python 2.
* @kind problem
* @tags maintainability
* correctness
* @problem.severity warning
* @sub-severity high
* @precision very-high
* @id py/truncated-division
*/
/**
* @name Result of integer division may be truncated
* @description The arguments to a division statement may be integers, which
* may cause the result to be truncated in Python 2.
* @kind problem
* @tags maintainability
* correctness
* @problem.severity warning
* @sub-severity high
* @precision very-high
* @id py/truncated-division
*/
import python
from BinaryExpr div, ControlFlowNode left, ControlFlowNode right
where
// Only relevant for Python 2, as all later versions implement true division
major_version() = 2
and
major_version() = 2 and
exists(BinaryExprNode bin, Value lval, Value rval |
bin = div.getAFlowNode()
and bin.getNode().getOp() instanceof Div
and bin.getLeft().pointsTo(lval, left)
and lval.getClass() = ClassValue::int_()
and bin.getRight().pointsTo(rval, right)
and rval.getClass() = ClassValue::int_()
bin = div.getAFlowNode() and
bin.getNode().getOp() instanceof Div and
bin.getLeft().pointsTo(lval, left) and
lval.getClass() = ClassValue::int_() and
bin.getRight().pointsTo(rval, right) and
rval.getClass() = ClassValue::int_() and
// Ignore instances where integer division leaves no remainder
and not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0
and not bin.getNode().getEnclosingModule().hasFromFuture("division")
not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and
not bin.getNode().getEnclosingModule().hasFromFuture("division") and
// Filter out results wrapped in `int(...)`
and not exists(CallNode c |
c = ClassValue::int_().getACall()
and c.getAnArg() = bin
not exists(CallNode c |
c = ClassValue::int_().getACall() and
c.getAnArg() = bin
)
)
select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.",
left.getLocation(), "left", right.getLocation(), "right"
left.getLocation(), "left", right.getLocation(), "right"

View File

@@ -22,14 +22,13 @@ predicate string_const(Expr s) {
from StrConst s
where
// Implicitly concatenated string is in a list and that list contains at least one other string.
exists(List l, Expr other |
not s = other and
l.getAnElt() = s and
l.getAnElt() = other and
string_const(other)
) and
exists(s.getAnImplicitlyConcatenatedPart()) and
not s.isParenthesized()
// Implicitly concatenated string is in a list and that list contains at least one other string.
exists(List l, Expr other |
not s = other and
l.getAnElt() = s and
l.getAnElt() = other and
string_const(other)
) and
exists(s.getAnImplicitlyConcatenatedPart()) and
not s.isParenthesized()
select s, "Implicit string concatenation. Maybe missing a comma?"

View File

@@ -15,43 +15,47 @@ import python
/* f consists of a single return statement, whose value is a call. The arguments of the call are exactly the parameters of f */
predicate simple_wrapper(Lambda l, Expr wrapped) {
exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() |
wrapped = c.getFunc() and
count(f.getAnArg()) = count(c.getAnArg()) and
forall(int arg | exists(f.getArg(arg)) |
f.getArgName(arg) = ((Name)c.getArg(arg)).getId()) and
/* Either no **kwargs or they must match */
(not exists(f.getKwarg()) and not exists(c.getKwargs()) or
((Name)f.getKwarg()).getId() = ((Name)c.getKwargs()).getId()) and
/* Either no *args or they must match */
(not exists(f.getVararg()) and not exists(c.getStarargs()) or
((Name)f.getVararg()).getId() = ((Name)c.getStarargs()).getId()) and
/* No named parameters in call */
not exists(c.getAKeyword())
)
and
wrapped = c.getFunc() and
count(f.getAnArg()) = count(c.getAnArg()) and
forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and
/* Either no **kwargs or they must match */
(
not exists(f.getKwarg()) and not exists(c.getKwargs())
or
f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId()
) and
/* Either no *args or they must match */
(
not exists(f.getVararg()) and not exists(c.getStarargs())
or
f.getVararg().(Name).getId() = c.getStarargs().(Name).getId()
) and
/* No named parameters in call */
not exists(c.getAKeyword())
) and
// f is not necessarily a drop-in replacement for the lambda if there are default argument values
not exists(l.getArgs().getADefault())
}
/* The expression called will refer to the same object if evaluated when the lambda is created or when the lambda is executed. */
predicate unnecessary_lambda(Lambda l, Expr e) {
simple_wrapper(l, e) and
simple_wrapper(l, e) and
(
/* plain class */
exists(ClassValue c | e.pointsTo(c))
or
/* plain function */
exists(FunctionValue f | e.pointsTo(f))
or
/* bound-method of enclosing instance */
exists(ClassValue cls, Attribute a |
cls.getScope() = l.getScope().getScope() and a = e |
((Name)a.getObject()).getId() = "self" and
cls.hasAttribute(a.getName())
)
/* plain class */
exists(ClassValue c | e.pointsTo(c))
or
/* plain function */
exists(FunctionValue f | e.pointsTo(f))
or
/* bound-method of enclosing instance */
exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e |
a.getObject().(Name).getId() = "self" and
cls.hasAttribute(a.getName())
)
)
}
from Lambda l, Expr e
where unnecessary_lambda(l, e)
select l, "This 'lambda' is just a simple wrapper around a callable object. Use that object directly."
select l,
"This 'lambda' is just a simple wrapper around a callable object. Use that object directly."

View File

@@ -16,11 +16,12 @@
import python
import Expressions.CallArgs
from Call call, FunctionObject func, string name
where
illegally_named_parameter_objectapi(call, func, name) and
not func.isAbstract() and
not exists(FunctionObject overridden | func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name)
select
call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func, func.descriptiveString()
illegally_named_parameter_objectapi(call, func, name) and
not func.isAbstract() and
not exists(FunctionObject overridden |
func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name
)
select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func,
func.descriptiveString()

View File

@@ -18,29 +18,30 @@ import semmle.python.strings
predicate string_format(BinaryExpr operation, StrConst str, Value args, AstNode origin) {
operation.getOp() instanceof Mod and
exists(Value fmt, Context ctx |
operation.getLeft().pointsTo(ctx, fmt, str) and
operation.getRight().pointsTo(ctx, args, origin)
operation.getLeft().pointsTo(ctx, fmt, str) and
operation.getRight().pointsTo(ctx, args, origin)
)
}
int sequence_length(Value args) {
/* Guess length of sequence */
exists(Tuple seq, AstNode origin |
seq.pointsTo(args,origin) |
exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) |
result = strictcount(seq.getAnElt()) and
not seq.getAnElt() instanceof Starred
)
or
exists(ImmutableLiteral i |
i.getLiteralValue() = args |
result = 1
)
exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1)
}
from BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, string provided
where string_format(operation, fmt, args, origin) and slen = sequence_length(args) and alen = format_items(fmt) and slen != alen and
(if slen = 1 then provided = " is provided." else provided = " are provided.")
select operation, "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + slen.toString() + provided,
origin, "arguments",
fmt, fmt.getText()
from
BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin,
string provided
where
string_format(operation, fmt, args, origin) and
slen = sequence_length(args) and
alen = format_items(fmt) and
slen != alen and
(if slen = 1 then provided = " is provided." else provided = " are provided.")
select operation,
"Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " +
slen.toString() + provided, origin, "arguments", fmt, fmt.getText()

View File

@@ -16,15 +16,20 @@ import CallArgs
from Call call, FunctionObject func, string too, string should, int limit
where
(
too_many_args_objectapi(call, func, limit) and too = "too many arguments" and should = "no more than "
or
too_few_args_objectapi(call, func, limit) and too = "too few arguments" and should = "no fewer than "
) and
not func.isAbstract() and
not exists(FunctionObject overridden | func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden))
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
and not func.getName() = "__new__"
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString()
(
too_many_args_objectapi(call, func, limit) and
too = "too many arguments" and
should = "no more than "
or
too_few_args_objectapi(call, func, limit) and
too = "too few arguments" and
should = "no fewer than "
) and
not func.isAbstract() and
not exists(FunctionObject overridden |
func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden)
) and
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
not func.getName() = "__new__"
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func,
func.descriptiveString()

View File

@@ -11,8 +11,9 @@ import semmle.python.filters.GeneratedCode
import semmle.python.filters.Tests
predicate classify(File f, string tag) {
f instanceof GeneratedFile and tag = "generated" or
exists (TestScope t | t.getLocation().getFile() = f) and tag = "test"
f instanceof GeneratedFile and tag = "generated"
or
exists(TestScope t | t.getLocation().getFile() = f) and tag = "test"
}
from File f, string tag

View File

@@ -4,6 +4,7 @@
* @kind problem
* @id py/not-generated-file-filter
*/
import python
import external.DefectFilter
import semmle.python.filters.GeneratedCode

View File

@@ -4,6 +4,7 @@
* @kind problem
* @id py/not-test-file-filter
*/
import python
import external.DefectFilter
import semmle.python.filters.Tests

View File

@@ -13,20 +13,21 @@
import python
predicate explicitly_returns_non_none(Function func) {
exists(Return return | return.getScope() = func and
exists(Expr val |
val= return.getValue() |
not val instanceof None
)
exists(Return return |
return.getScope() = func and
exists(Expr val | val = return.getValue() | not val instanceof None)
)
}
predicate has_implicit_return(Function func) {
exists(ControlFlowNode fallthru | fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable()) or
exists(ControlFlowNode fallthru |
fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable()
)
or
exists(Return return | return.getScope() = func and not exists(return.getValue()))
}
from Function func
where explicitly_returns_non_none(func) and has_implicit_return(func)
select func, "Mixing implicit and explicit returns may indicate an error as implicit returns always return None."
select func,
"Mixing implicit and explicit returns may indicate an error as implicit returns always return None."

View File

@@ -12,13 +12,13 @@
import python
predicate slice_method_name(string name) {
name = "__getslice__" or name = "__setslice__" or name = "__delslice__"
name = "__getslice__" or name = "__setslice__" or name = "__delslice__"
}
from PythonFunctionValue f, string meth
where f.getScope().isMethod() and not f.isOverridingMethod() and
slice_method_name(meth) and f.getName() = meth
select f, meth + " method has been deprecated since Python 2.0"
where
f.getScope().isMethod() and
not f.isOverridingMethod() and
slice_method_name(meth) and
f.getName() = meth
select f, meth + " method has been deprecated since Python 2.0"

View File

@@ -22,44 +22,87 @@ private predicate indexing_method(string name) {
}
private predicate arithmetic_method(string name) {
name = "__add__" or name = "__sub__" or name = "__div__" or
name = "__pos__" or name = "__abs__" or name = "__floordiv__" or
name = "__div__" or name = "__divmod__" or name = "__lshift__" or
name = "__and__" or name = "__or__"or name = "__xor__" or name = "__rshift__" or
name = "__pow__" or name = "__mul__" or name = "__neg__" or
name = "__radd__" or name = "__rsub__" or name = "__rdiv__" or
name = "__rfloordiv__" or name = "__rdiv__" or name = "__rlshift__" or
name = "__rand__" or name = "__ror__"or name = "__rxor__" or name = "__rrshift__" or
name = "__rpow__" or name = "__rmul__" or name = "__truediv__" or name = "__rtruediv__" or
name = "__iadd__" or name = "__isub__" or name = "__idiv__" or
name = "__ifloordiv__" or name = "__idiv__" or name = "__ilshift__" or
name = "__iand__" or name = "__ior__"or name = "__ixor__" or name = "__irshift__" or
name = "__ipow__" or name = "__imul__" or name = "__itruediv__"
name = "__add__" or
name = "__sub__" or
name = "__div__" or
name = "__pos__" or
name = "__abs__" or
name = "__floordiv__" or
name = "__div__" or
name = "__divmod__" or
name = "__lshift__" or
name = "__and__" or
name = "__or__" or
name = "__xor__" or
name = "__rshift__" or
name = "__pow__" or
name = "__mul__" or
name = "__neg__" or
name = "__radd__" or
name = "__rsub__" or
name = "__rdiv__" or
name = "__rfloordiv__" or
name = "__rdiv__" or
name = "__rlshift__" or
name = "__rand__" or
name = "__ror__" or
name = "__rxor__" or
name = "__rrshift__" or
name = "__rpow__" or
name = "__rmul__" or
name = "__truediv__" or
name = "__rtruediv__" or
name = "__iadd__" or
name = "__isub__" or
name = "__idiv__" or
name = "__ifloordiv__" or
name = "__idiv__" or
name = "__ilshift__" or
name = "__iand__" or
name = "__ior__" or
name = "__ixor__" or
name = "__irshift__" or
name = "__ipow__" or
name = "__imul__" or
name = "__itruediv__"
}
private predicate ordering_method(string name) {
name = "__lt__" or name = "__le__" or name = "__gt__" or name = "__ge__" or
name = "__lt__"
or
name = "__le__"
or
name = "__gt__"
or
name = "__ge__"
or
name = "__cmp__" and major_version() = 2
}
private predicate cast_method(string name) {
name = "__nonzero__" and major_version() = 2 or
name = "__bool__" or
name = "__int__" or name = "__float__" or
name = "__long__" or
name = "__trunc__" or
name = "__nonzero__" and major_version() = 2
or
name = "__bool__"
or
name = "__int__"
or
name = "__float__"
or
name = "__long__"
or
name = "__trunc__"
or
name = "__complex__"
}
predicate correct_raise(string name, ClassObject ex) {
ex.getAnImproperSuperType() = theTypeErrorType()
and
ex.getAnImproperSuperType() = theTypeErrorType() and
(
name = "__copy__" or
name = "__deepcopy__" or
name = "__call__" or
indexing_method(name) or
attribute_method(name)
name = "__copy__" or
name = "__deepcopy__" or
name = "__call__" or
indexing_method(name) or
attribute_method(name)
)
or
preferred_raise(name, ex)
@@ -84,11 +127,11 @@ predicate no_need_to_raise(string name, string message) {
}
predicate is_abstract(FunctionObject func) {
((Name)func.getFunction().getADecorator()).getId().matches("%abstract%")
func.getFunction().getADecorator().(Name).getId().matches("%abstract%")
}
predicate always_raises(FunctionObject f, ClassObject ex) {
ex = f.getARaisedType() and
ex = f.getARaisedType() and
strictcount(f.getARaisedType()) = 1 and
not exists(f.getFunction().getANormalExit()) and
/* raising StopIteration is equivalent to a return in a generator */
@@ -96,17 +139,17 @@ predicate always_raises(FunctionObject f, ClassObject ex) {
}
from FunctionObject f, ClassObject cls, string message
where f.getFunction().isSpecialMethod() and
not is_abstract(f) and
always_raises(f, cls) and
(
no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError"
or
not correct_raise(f.getName(), cls) and not cls.getName() = "NotImplementedError"
and
exists(ClassObject preferred |
preferred_raise(f.getName(), preferred) |
message = "raise " + preferred.getName() + " instead"
where
f.getFunction().isSpecialMethod() and
not is_abstract(f) and
always_raises(f, cls) and
(
no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError"
or
not correct_raise(f.getName(), cls) and
not cls.getName() = "NotImplementedError" and
exists(ClassObject preferred | preferred_raise(f.getName(), preferred) |
message = "raise " + preferred.getName() + " instead"
)
)
)
select f, "Function always raises $@; " + message, cls, cls.toString()

View File

@@ -14,14 +14,18 @@ import Expressions.CallArgs
from Call call, FunctionObject func, FunctionObject overridden, string problem
where
func.overrides(overridden) and (
wrong_args_objectapi(call, func, _, problem) and correct_args_if_called_as_method_objectapi(call, overridden)
or
exists(string name |
illegally_named_parameter_objectapi(call, func, name) and problem = "an argument named '" + name + "'" and
overridden.getFunction().getAnArg().(Name).getId() = name
func.overrides(overridden) and
(
wrong_args_objectapi(call, func, _, problem) and
correct_args_if_called_as_method_objectapi(call, overridden)
or
exists(string name |
illegally_named_parameter_objectapi(call, func, name) and
problem = "an argument named '" + name + "'" and
overridden.getFunction().getAnArg().(Name).getId() = name
)
)
)
select func, "Overriding method signature does not match $@, where it is passed " + problem + ". Overridden method $@ is correctly specified.",
call, "here", overridden, overridden.descriptiveString()
select func,
"Overriding method signature does not match $@, where it is passed " + problem +
". Overridden method $@ is correctly specified.", call, "here", overridden,
overridden.descriptiveString()

View File

@@ -15,21 +15,23 @@ import Expressions.CallArgs
from Call call, FunctionObject func, FunctionObject overriding, string problem
where
not func.getName() = "__init__" and
overriding.overrides(func) and
call = overriding.getAMethodCall().getNode() and
correct_args_if_called_as_method_objectapi(call, overriding) and
(
arg_count_objectapi(call)+1 < func.minParameters() and problem = "too few arguments"
or
arg_count_objectapi(call) >= func.maxParameters() and problem = "too many arguments"
or
exists(string name | call.getAKeyword().getArg() = name and
overriding.getFunction().getAnArg().(Name).getId() = name and
not func.getFunction().getAnArg().(Name).getId() = name and
problem = "an argument named '" + name + "'"
not func.getName() = "__init__" and
overriding.overrides(func) and
call = overriding.getAMethodCall().getNode() and
correct_args_if_called_as_method_objectapi(call, overriding) and
(
arg_count_objectapi(call) + 1 < func.minParameters() and problem = "too few arguments"
or
arg_count_objectapi(call) >= func.maxParameters() and problem = "too many arguments"
or
exists(string name |
call.getAKeyword().getArg() = name and
overriding.getFunction().getAnArg().(Name).getId() = name and
not func.getFunction().getAnArg().(Name).getId() = name and
problem = "an argument named '" + name + "'"
)
)
)
select func, "Overridden method signature does not match $@, where it is passed " + problem + ". Overriding method $@ matches the call.",
call, "call", overriding, overriding.descriptiveString()
select func,
"Overridden method signature does not match $@, where it is passed " + problem +
". Overriding method $@ matches the call.", call, "call", overriding,
overriding.descriptiveString()

View File

@@ -13,6 +13,7 @@
import python
from Function f
where f.isInitMethod() and
(exists(Yield y | y.getScope() = f) or exists(YieldFrom y| y.getScope() = f))
where
f.isInitMethod() and
(exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f))
select f, "__init__ method is a generator."

View File

@@ -13,21 +13,20 @@
import python
Function iter_method(ClassObject t) {
result = ((FunctionObject)t.lookupAttribute("__iter__")).getFunction()
result = t.lookupAttribute("__iter__").(FunctionObject).getFunction()
}
predicate is_self(Name value, Function f) {
value.getVariable() = ((Name)f.getArg(0)).getVariable()
}
predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() }
predicate returns_non_self(Function f) {
exists(f.getFallthroughNode())
or
exists(Return r | r.getScope() = f and not is_self(r.getValue(), f))
or
exists(Return r | r.getScope() = f and not exists(r.getValue()))
exists(f.getFallthroughNode())
or
exists(Return r | r.getScope() = f and not is_self(r.getValue(), f))
or
exists(Return r | r.getScope() = f and not exists(r.getValue()))
}
from ClassObject t, Function iter
where t.isIterator() and iter = iter_method(t) and returns_non_self(iter)
select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.", iter, iter.getName()
select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.",
iter, iter.getName()

View File

@@ -40,9 +40,11 @@ where
(
if exists(f.getArgName(0))
then
message = "Class methods or methods of a type deriving from type should have 'cls', rather than '"
+ f.getArgName(0) + "', as their first parameter."
message =
"Class methods or methods of a type deriving from type should have 'cls', rather than '" +
f.getArgName(0) + "', as their first parameter."
else
message = "Class methods or methods of a type deriving from type should have 'cls' as their first parameter."
message =
"Class methods or methods of a type deriving from type should have 'cls' as their first parameter."
)
select f, message

View File

@@ -45,10 +45,12 @@ where
(
if exists(f.getArgName(0))
then
message = "Normal methods should have 'self', rather than '" + f.getArgName(0) +
message =
"Normal methods should have 'self', rather than '" + f.getArgName(0) +
"', as their first parameter."
else
message = "Normal methods should have at least one parameter (the first of which should be 'self')."
message =
"Normal methods should have at least one parameter (the first of which should be 'self')."
) and
not f.hasVarArg()
) and

View File

@@ -16,6 +16,9 @@
import python
from FunctionObject method
where exists(ClassObject c | c.declaredAttribute("__del__") = method and
method.getFunction().getMetrics().getCyclomaticComplexity() > 3)
where
exists(ClassObject c |
c.declaredAttribute("__del__") = method and
method.getFunction().getMetrics().getCyclomaticComplexity() > 3
)
select method, "Overly complex '__del__' method."

View File

@@ -15,15 +15,16 @@ import python
predicate returns_tuple_of_size(Function func, int size, AstNode origin) {
exists(Return return, TupleValue val |
return.getScope() = func and
return.getValue().pointsTo(val, origin) |
return.getValue().pointsTo(val, origin)
|
size = val.length()
)
}
from Function func, int s1, int s2, AstNode t1, AstNode t2
where
returns_tuple_of_size(func, s1, t1) and
returns_tuple_of_size(func, s2, t2) and
s1 < s2
select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2, "tuple of size " + s2
select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2,
"tuple of size " + s2

View File

@@ -21,28 +21,34 @@ predicate meaningful_return_value(Expr val) {
or
val instanceof BooleanLiteral
or
exists(FunctionObject callee | val = callee.getACall().getNode() and returns_meaningful_value(callee))
exists(FunctionObject callee |
val = callee.getACall().getNode() and returns_meaningful_value(callee)
)
or
not exists(FunctionObject callee | val = callee.getACall().getNode()) and not val instanceof Name
}
/* Value is used before returning, and thus its value is not lost if ignored */
predicate used_value(Expr val) {
exists(LocalVariable var, Expr other | var.getAnAccess() = val and other = var.getAnAccess() and not other = val)
exists(LocalVariable var, Expr other |
var.getAnAccess() = val and other = var.getAnAccess() and not other = val
)
}
predicate returns_meaningful_value(FunctionObject f) {
not exists(f.getFunction().getFallthroughNode())
and
predicate returns_meaningful_value(FunctionObject f) {
not exists(f.getFunction().getFallthroughNode()) and
(
exists(Return ret, Expr val | ret.getScope() = f.getFunction() and val = ret.getValue() |
meaningful_return_value(val) and
not used_value(val)
)
or
/* Is f a builtin function that returns something other than None?
* Ignore __import__ as it is often called purely for side effects */
f.isC() and f.getAnInferredReturnType() != theNoneType() and not f.getName() = "__import__"
exists(Return ret, Expr val | ret.getScope() = f.getFunction() and val = ret.getValue() |
meaningful_return_value(val) and
not used_value(val)
)
or
/*
* Is f a builtin function that returns something other than None?
* Ignore __import__ as it is often called purely for side effects
*/
f.isC() and f.getAnInferredReturnType() != theNoneType() and not f.getName() = "__import__"
)
}
@@ -56,17 +62,19 @@ predicate wrapped_in_try_except(ExprStmt call) {
}
from ExprStmt call, FunctionObject callee, float percentage_used, int total
where call.getValue() = callee.getACall().getNode() and returns_meaningful_value(callee) and
not wrapped_in_try_except(call) and
exists(int unused |
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
total = count(callee.getACall()) |
percentage_used = (100.0*(total-unused)/total).floor()
) and
/* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */
percentage_used >= 75 and
total >= 5
select call, "Call discards return value of function $@. The result is used in " + percentage_used.toString() + "% of calls.",
callee, callee.getName()
where
call.getValue() = callee.getACall().getNode() and
returns_meaningful_value(callee) and
not wrapped_in_try_except(call) and
exists(int unused |
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
total = count(callee.getACall())
|
percentage_used = (100.0 * (total - unused) / total).floor()
) and
/* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */
percentage_used >= 75 and
total >= 5
select call,
"Call discards return value of function $@. The result is used in " + percentage_used.toString() +
"% of calls.", callee, callee.getName()

View File

@@ -6,7 +6,6 @@
* @problem.severity warning
* @tags reliability
* correctness
* @problem.severity warning
* @sub-severity high
* @precision very-high
* @id py/inheritance/signature-mismatch
@@ -17,19 +16,20 @@ import Expressions.CallArgs
from FunctionObject base, PyFunctionObject derived
where
not exists(base.getACall()) and
not exists(FunctionObject a_derived |
a_derived.overrides(base) and
exists(a_derived.getACall())
) and
not derived.getFunction().isSpecialMethod() and
derived.getName() != "__init__" and
derived.isNormalMethod() and
not derived.getFunction().isSpecialMethod() and
// call to overrides distributed for efficiency
(
(derived.overrides(base) and derived.minParameters() > base.maxParameters())
or
(derived.overrides(base) and derived.maxParameters() < base.minParameters())
)
select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.", base, "overridden method"
not exists(base.getACall()) and
not exists(FunctionObject a_derived |
a_derived.overrides(base) and
exists(a_derived.getACall())
) and
not derived.getFunction().isSpecialMethod() and
derived.getName() != "__init__" and
derived.isNormalMethod() and
not derived.getFunction().isSpecialMethod() and
// call to overrides distributed for efficiency
(
derived.overrides(base) and derived.minParameters() > base.maxParameters()
or
derived.overrides(base) and derived.maxParameters() < base.minParameters()
)
select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.",
base, "overridden method"

View File

@@ -12,105 +12,102 @@
import python
predicate is_unary_op(string name) {
name = "__del__" or
name = "__repr__" or
name = "__str__" or
name = "__hash__" or
name = "__bool__" or
name = "__nonzero__" or
name = "__unicode__" or
name = "__len__" or
name = "__iter__" or
name = "__reversed__" or
name = "__neg__" or
name = "__pos__" or
name = "__abs__" or
name = "__invert__" or
name = "__complex__" or
name = "__int__" or
name = "__float__" or
name = "__long__" or
name = "__oct__" or
name = "__hex__" or
name = "__index__" or
name = "__enter__"
name = "__del__" or
name = "__repr__" or
name = "__str__" or
name = "__hash__" or
name = "__bool__" or
name = "__nonzero__" or
name = "__unicode__" or
name = "__len__" or
name = "__iter__" or
name = "__reversed__" or
name = "__neg__" or
name = "__pos__" or
name = "__abs__" or
name = "__invert__" or
name = "__complex__" or
name = "__int__" or
name = "__float__" or
name = "__long__" or
name = "__oct__" or
name = "__hex__" or
name = "__index__" or
name = "__enter__"
}
predicate is_binary_op(string name) {
name = "__lt__" or
name = "__le__" or
name = "__eq__" or
name = "__ne__" or
name = "__gt__" or
name = "__ge__" or
name = "__cmp__" or
name = "__rcmp__" or
name = "__getattr___" or
name = "__getattribute___" or
name = "__delattr__" or
name = "__delete__" or
name = "__instancecheck__" or
name = "__subclasscheck__" or
name = "__getitem__" or
name = "__delitem__" or
name = "__contains__" or
name = "__add__" or
name = "__sub__" or
name = "__mul__" or
name = "__floordiv__" or
name = "__div__" or
name = "__truediv__" or
name = "__mod__" or
name = "__divmod__" or
name = "__lshift__" or
name = "__rshift__" or
name = "__and__" or
name = "__xor__" or
name = "__or__" or
name = "__radd__" or
name = "__rsub__" or
name = "__rmul__" or
name = "__rfloordiv__" or
name = "__rdiv__" or
name = "__rtruediv__" or
name = "__rmod__" or
name = "__rdivmod__" or
name = "__rpow__" or
name = "__rlshift__" or
name = "__rrshift__" or
name = "__rand__" or
name = "__rxor__" or
name = "__ror__" or
name = "__iadd__" or
name = "__isub__" or
name = "__imul__" or
name = "__ifloordiv__" or
name = "__idiv__" or
name = "__itruediv__" or
name = "__imod__" or
name = "__idivmod__" or
name = "__ipow__" or
name = "__ilshift__" or
name = "__irshift__" or
name = "__iand__" or
name = "__ixor__" or
name = "__ior__" or
name = "__coerce__"
name = "__lt__" or
name = "__le__" or
name = "__eq__" or
name = "__ne__" or
name = "__gt__" or
name = "__ge__" or
name = "__cmp__" or
name = "__rcmp__" or
name = "__getattr___" or
name = "__getattribute___" or
name = "__delattr__" or
name = "__delete__" or
name = "__instancecheck__" or
name = "__subclasscheck__" or
name = "__getitem__" or
name = "__delitem__" or
name = "__contains__" or
name = "__add__" or
name = "__sub__" or
name = "__mul__" or
name = "__floordiv__" or
name = "__div__" or
name = "__truediv__" or
name = "__mod__" or
name = "__divmod__" or
name = "__lshift__" or
name = "__rshift__" or
name = "__and__" or
name = "__xor__" or
name = "__or__" or
name = "__radd__" or
name = "__rsub__" or
name = "__rmul__" or
name = "__rfloordiv__" or
name = "__rdiv__" or
name = "__rtruediv__" or
name = "__rmod__" or
name = "__rdivmod__" or
name = "__rpow__" or
name = "__rlshift__" or
name = "__rrshift__" or
name = "__rand__" or
name = "__rxor__" or
name = "__ror__" or
name = "__iadd__" or
name = "__isub__" or
name = "__imul__" or
name = "__ifloordiv__" or
name = "__idiv__" or
name = "__itruediv__" or
name = "__imod__" or
name = "__idivmod__" or
name = "__ipow__" or
name = "__ilshift__" or
name = "__irshift__" or
name = "__iand__" or
name = "__ixor__" or
name = "__ior__" or
name = "__coerce__"
}
predicate is_ternary_op(string name) {
name = "__setattr__" or
name = "__set__" or
name = "__setitem__" or
name = "__getslice__" or
name = "__delslice__"
name = "__setattr__" or
name = "__set__" or
name = "__setitem__" or
name = "__getslice__" or
name = "__delslice__"
}
predicate is_quad_op(string name) {
name = "__setslice__" or name = "__exit__"
}
predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" }
int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
cls.declaredAttribute(name) = f and
@@ -125,52 +122,62 @@ int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
)
}
predicate incorrect_special_method_defn(PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner) {
exists(int required |
required = argument_count(func, name, owner) |
/* actual_non_default <= actual */
if required > func.maxParameters() then
(message = "Too few parameters" and show_counts = true)
else if required < func.minParameters() then
(message = "Too many parameters" and show_counts = true)
else if (func.minParameters() < required and not func.getScope().hasVarArg()) then
(message = (required -func.minParameters()) + " default values(s) will never be used" and show_counts = false)
else
none()
)
predicate incorrect_special_method_defn(
PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner
) {
exists(int required | required = argument_count(func, name, owner) |
/* actual_non_default <= actual */
if required > func.maxParameters()
then message = "Too few parameters" and show_counts = true
else
if required < func.minParameters()
then message = "Too many parameters" and show_counts = true
else
if func.minParameters() < required and not func.getScope().hasVarArg()
then
message = (required - func.minParameters()) + " default values(s) will never be used" and
show_counts = false
else none()
)
}
predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
owner.declaredAttribute("__pow__") = func and
(
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
or
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
or
func.minParameters() < 2 and message = (2 - func.minParameters()) + " default value(s) will never be used" and show_counts = false
or
func.minParameters() = 3 and message = "Third parameter to __pow__ should have a default value" and show_counts = false
)
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
or
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
or
func.minParameters() < 2 and
message = (2 - func.minParameters()) + " default value(s) will never be used" and
show_counts = false
or
func.minParameters() = 3 and
message = "Third parameter to __pow__ should have a default value" and
show_counts = false
)
}
predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
owner.declaredAttribute("__get__") = func and
(
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
or
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
or
func.minParameters() < 2 and not func.getScope().hasVarArg() and
message = (2 - func.minParameters()) + " default value(s) will never be used" and show_counts = false
)
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
or
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
or
func.minParameters() < 2 and
not func.getScope().hasVarArg() and
message = (2 - func.minParameters()) + " default value(s) will never be used" and
show_counts = false
)
}
string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) {
exists(int i | i = argument_count(f, name, owner) |
result = i.toString()
)
or
owner.declaredAttribute(name) = f and (name = "__get__" or name = "__pow__") and result = "2 or 3"
exists(int i | i = argument_count(f, name, owner) | result = i.toString())
or
owner.declaredAttribute(name) = f and
(name = "__get__" or name = "__pow__") and
result = "2 or 3"
}
string has_parameters(PythonFunctionValue f) {
@@ -183,18 +190,23 @@ string has_parameters(PythonFunctionValue f) {
)
}
from PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, ClassValue owner
where
(
incorrect_special_method_defn(f, message, show_counts, name, owner)
or
incorrect_pow(f, message, show_counts, owner) and name = "__pow__"
or
incorrect_get(f, message, show_counts, owner) and name = "__get__"
)
and
(
show_counts = false and sizes = "" or
show_counts = true and sizes = ", which has " + has_parameters(f) + ", but should have " + should_have_parameters(f, name, owner)
)
from
PythonFunctionValue f, string message, string sizes, boolean show_counts, string name,
ClassValue owner
where
(
incorrect_special_method_defn(f, message, show_counts, name, owner)
or
incorrect_pow(f, message, show_counts, owner) and name = "__pow__"
or
incorrect_get(f, message, show_counts, owner) and name = "__get__"
) and
(
show_counts = false and sizes = ""
or
show_counts = true and
sizes =
", which has " + has_parameters(f) + ", but should have " +
should_have_parameters(f, name, owner)
)
select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName()

View File

@@ -13,9 +13,11 @@ import python
import Testing.Mox
predicate is_used(Call c) {
exists(Expr outer | outer != c and outer.containsInScope(c) | outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript)
exists(Expr outer | outer != c and outer.containsInScope(c) |
outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript
)
or
exists(Stmt s |
exists(Stmt s |
c = s.getASubExpression() and
not s instanceof ExprStmt and
/* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */
@@ -24,11 +26,13 @@ predicate is_used(Call c) {
}
from Call c, FunctionValue func
where
/* Call result is used, but callee is a procedure */
is_used(c) and c.getFunc().pointsTo(func) and func.getScope().isProcedure() and
/* All callees are procedures */
forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and
/* Mox return objects have an `AndReturn` method */
not useOfMoxInModule(c.getEnclosingModule())
where
/* Call result is used, but callee is a procedure */
is_used(c) and
c.getFunc().pointsTo(func) and
func.getScope().isProcedure() and
/* All callees are procedures */
forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and
/* Mox return objects have an `AndReturn` method */
not useOfMoxInModule(c.getEnclosingModule())
select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName()

View File

@@ -60,7 +60,8 @@ predicate deprecated_module(string name, string instead, int major, int minor) {
string deprecation_message(string mod) {
exists(int major, int minor | deprecated_module(mod, _, major, minor) |
result = "The " + mod + " module was deprecated in version " + major.toString() + "." +
result =
"The " + mod + " module was deprecated in version " + major.toString() + "." +
minor.toString() + "."
)
}
@@ -77,8 +78,7 @@ from ImportExpr imp, string name, string instead
where
name = imp.getName() and
deprecated_module(name, instead, _, _) and
not exists(Try try, ExceptStmt except | except = try.getAHandler()
|
not exists(Try try, ExceptStmt except | except = try.getAHandler() |
except.getType().pointsTo(ClassValue::importError()) and
except.containsInScope(imp)
)

View File

@@ -13,4 +13,4 @@
import python
from EncodingError error
select error, error.getMessage()
select error, error.getMessage()

View File

@@ -13,10 +13,13 @@
import python
predicate shadowsImport(Variable l) {
exists(Import i, Name shadow | shadow = i.getAName().getAsname() and shadow.getId() = l.getId() and i.getScope() = l.getScope().getScope*())
exists(Import i, Name shadow |
shadow = i.getAName().getAsname() and
shadow.getId() = l.getId() and
i.getScope() = l.getScope().getScope*()
)
}
from Variable l, Name defn
where shadowsImport(l) and defn.defines(l) and exists(For for | defn = for.getTarget())
select defn, "Loop variable '" + l.getId() + "' shadows an import"

View File

@@ -14,4 +14,3 @@ import python
from ImportStar i
select i, "Using 'from ... import *' pollutes the namespace"

View File

@@ -12,9 +12,10 @@
import python
predicate import_and_import_from(Import i1, Import i2, Module m) {
i1.getEnclosingModule() = i2.getEnclosingModule() and
exists (ImportExpr e1, ImportExpr e2, ImportMember im |
e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() |
i1.getEnclosingModule() = i2.getEnclosingModule() and
exists(ImportExpr e1, ImportExpr e2, ImportMember im |
e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule()
|
e1.getName() = m.getName() and e2.getName() = m.getName()
)
}

View File

@@ -11,16 +11,15 @@
* @id py/multiple-imports-on-line
*/
/* Look for imports of the form:
import modA, modB
(Imports should be one per line according PEP 8)
*/
/*
* Look for imports of the form:
* import modA, modB
* (Imports should be one per line according PEP 8)
*/
import python
predicate multiple_import(Import imp) {
count(imp.getAName()) > 1 and not imp.isFromImport()
}
predicate multiple_import(Import imp) { count(imp.getAName()) > 1 and not imp.isFromImport() }
from Import i
where multiple_import(i)

View File

@@ -14,7 +14,8 @@ import python
predicate modules_imports_itself(ImportingStmt i, ModuleValue m) {
i.getEnclosingModule() = m.getScope() and
m = max(string s, ModuleValue m_ |
m =
max(string s, ModuleValue m_ |
s = i.getAnImportedModuleName() and
m_.importedAs(s)
|

Some files were not shown because too many files have changed in this diff Show More