Python: Autoformat everything using qlformat.

Will need subsequent PRs fixing up test failures (due to deprecated
methods moving around), but other than that everything should be
straight-forward.
This commit is contained in:
Taus Brock-Nannestad
2020-07-07 15:43:52 +02:00
parent 993506d781
commit f07a7bf8cf
602 changed files with 26777 additions and 26790 deletions

View File

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

View File

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

View File

@@ -14,8 +14,8 @@ import python
from If i
where
not exists(Stmt s |
i.getStmt(_) = s and
not s instanceof Pass
)
not exists(Stmt s |
i.getStmt(_) = s and
not s instanceof Pass
)
select i

View File

@@ -14,6 +14,6 @@ import python
from ClassObject sub, ClassObject base
where
base.getName() = "MyClass" and
sub.getABaseType() = base
base.getName() = "MyClass" and
sub.getABaseType() = base
select sub

View File

@@ -10,6 +10,6 @@ import python
from AstNode call, PythonFunctionValue method
where
method.getQualifiedName() = "MyClass.methodName" and
method.getACall().getNode() = call
method.getQualifiedName() = "MyClass.methodName" and
method.getACall().getNode() = call
select call

View File

@@ -11,6 +11,6 @@ import python
from Call new, ClassValue cls
where
cls.getName() = "MyClass" and
new.getFunc().pointsTo(cls)
cls.getName() = "MyClass" and
new.getFunc().pointsTo(cls)
select new

View File

@@ -10,6 +10,6 @@ import python
from FunctionObject override, FunctionObject base
where
base.getQualifiedName() = "MyClass.methodName" and
override.overrides(base)
base.getQualifiedName() = "MyClass.methodName" and
override.overrides(base)
select override

View File

@@ -9,9 +9,9 @@ import python
from AstNode print
where
/* Python 2 without `from __future__ import print_function` */
print instanceof Print
or
/* Python 3 or with `from __future__ import print_function` */
print.(Call).getFunc().pointsTo(Value::named("print"))
/* Python 2 without `from __future__ import print_function` */
print instanceof Print
or
/* Python 3 or with `from __future__ import print_function` */
print.(Call).getFunc().pointsTo(Value::named("print"))
select print

View File

@@ -9,12 +9,12 @@
import python
predicate is_private(Attribute a) {
a.getName().matches("\\_%") and
not a.getName().matches("\\_\\_%\\_\\_")
a.getName().matches("\\_%") and
not a.getName().matches("\\_\\_%\\_\\_")
}
from Attribute access
where
is_private(access) and
not access.getObject().(Name).getId() = "self"
is_private(access) and
not access.getObject().(Name).getId() = "self"
select access

View File

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

View File

@@ -13,6 +13,6 @@ import python
from SubscriptNode store
where
store.isStore() and
store.getIndex().pointsTo(Value::named("None"))
store.isStore() and
store.getIndex().pointsTo(Value::named("None"))
select store

View File

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

View File

@@ -3,140 +3,140 @@ private import semmle.python.pointsto.PointsTo
/** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */
class CheckClass extends ClassObject {
private predicate ofInterest() {
not this.unknowableAttributes() and
not this.getPyClass().isProbableMixin() and
this.getPyClass().isPublic() and
not this.getPyClass().getScope() instanceof Function and
not this.probablyAbstract() and
not this.declaresAttribute("__new__") and
not this.selfDictAssigns() and
not this.lookupAttribute("__getattribute__") != object_getattribute() and
not this.hasAttribute("__getattr__") and
not this.selfSetattr() and
/* If class overrides object.__init__, but we can't resolve it to a Python function then give up */
forall(ClassObject sup |
sup = this.getAnImproperSuperType() and
sup.declaresAttribute("__init__") and
not sup = theObjectType()
|
sup.declaredAttribute("__init__") instanceof PyFunctionObject
)
}
private predicate ofInterest() {
not this.unknowableAttributes() and
not this.getPyClass().isProbableMixin() and
this.getPyClass().isPublic() and
not this.getPyClass().getScope() instanceof Function and
not this.probablyAbstract() and
not this.declaresAttribute("__new__") and
not this.selfDictAssigns() and
not this.lookupAttribute("__getattribute__") != object_getattribute() and
not this.hasAttribute("__getattr__") and
not this.selfSetattr() and
/* If class overrides object.__init__, but we can't resolve it to a Python function then give up */
forall(ClassObject sup |
sup = this.getAnImproperSuperType() and
sup.declaresAttribute("__init__") and
not sup = theObjectType()
|
sup.declaredAttribute("__init__") instanceof PyFunctionObject
)
}
predicate alwaysDefines(string name) {
auto_name(name) or
this.hasAttribute(name) or
this.getAnImproperSuperType().assignedInInit(name) or
this.getMetaClass().assignedInInit(name)
}
predicate alwaysDefines(string name) {
auto_name(name) or
this.hasAttribute(name) or
this.getAnImproperSuperType().assignedInInit(name) or
this.getMetaClass().assignedInInit(name)
}
predicate sometimesDefines(string name) {
this.alwaysDefines(name)
predicate sometimesDefines(string name) {
this.alwaysDefines(name)
or
exists(SelfAttributeStore sa |
sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass()
|
name = sa.getName()
)
}
private predicate selfDictAssigns() {
exists(Assign a, SelfAttributeRead self_dict, Subscript sub |
self_dict.getName() = "__dict__" and
(
self_dict = sub.getObject()
or
exists(SelfAttributeStore sa |
sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass()
|
name = sa.getName()
/* Indirect assignment via temporary variable */
exists(SsaVariable v |
v.getAUse() = sub.getObject().getAFlowNode() and
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
)
}
) and
a.getATarget() = sub and
exists(FunctionObject meth |
meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction()
)
)
}
private predicate selfDictAssigns() {
exists(Assign a, SelfAttributeRead self_dict, Subscript sub |
self_dict.getName() = "__dict__" and
(
self_dict = sub.getObject()
or
/* Indirect assignment via temporary variable */
exists(SsaVariable v |
v.getAUse() = sub.getObject().getAFlowNode() and
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
)
) and
a.getATarget() = sub and
exists(FunctionObject meth |
meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction()
)
)
}
pragma[nomagic]
private predicate monkeyPatched(string name) {
exists(Attribute a |
a.getCtx() instanceof Store and
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
a.getName() = name
)
}
pragma[nomagic]
private predicate monkeyPatched(string name) {
exists(Attribute a |
a.getCtx() instanceof Store and
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
a.getName() = name
)
}
private predicate selfSetattr() {
exists(Call c, Name setattr, Name self, Function method |
(
method.getScope() = this.getPyClass() or
method.getScope() = this.getASuperType().getPyClass()
) and
c.getScope() = method and
c.getFunc() = setattr and
setattr.getId() = "setattr" and
c.getArg(0) = self and
self.getId() = "self"
)
}
private predicate selfSetattr() {
exists(Call c, Name setattr, Name self, Function method |
(
method.getScope() = this.getPyClass() or
method.getScope() = this.getASuperType().getPyClass()
) and
c.getScope() = method and
c.getFunc() = setattr and
setattr.getId() = "setattr" and
c.getArg(0) = self and
self.getId() = "self"
)
}
predicate interestingUndefined(SelfAttributeRead a) {
exists(string name | name = a.getName() |
interestingContext(a, name) and
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
)
}
predicate interestingUndefined(SelfAttributeRead a) {
exists(string name | name = a.getName() |
interestingContext(a, name) and
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
)
}
private predicate interestingContext(SelfAttributeRead a, string name) {
name = a.getName() and
this.ofInterest() and
this.getPyClass() = a.getScope().getScope() and
not a.locallyDefined() and
not a.guardedByHasattr() and
a.getScope().isPublic() and
not this.monkeyPatched(name) and
not attribute_assigned_in_method(lookupAttribute("setUp"), name)
}
private predicate interestingContext(SelfAttributeRead a, string name) {
name = a.getName() and
this.ofInterest() and
this.getPyClass() = a.getScope().getScope() and
not a.locallyDefined() and
not a.guardedByHasattr() and
a.getScope().isPublic() and
not this.monkeyPatched(name) and
not attribute_assigned_in_method(lookupAttribute("setUp"), name)
}
private predicate probablyAbstract() {
this.getName().matches("Abstract%")
or
this.isAbstract()
}
private predicate probablyAbstract() {
this.getName().matches("Abstract%")
or
this.isAbstract()
}
pragma[nomagic]
private predicate definitionInBlock(BasicBlock b, string name) {
exists(SelfAttributeStore sa |
sa.getAFlowNode().getBasicBlock() = b and
sa.getName() = name and
sa.getClass() = this.getPyClass()
)
or
exists(FunctionObject method | this.lookupAttribute(_) = method |
attribute_assigned_in_method(method, name) and
b = method.getACall().getBasicBlock()
)
}
pragma[nomagic]
private predicate definitionInBlock(BasicBlock b, string name) {
exists(SelfAttributeStore sa |
sa.getAFlowNode().getBasicBlock() = b and
sa.getName() = name and
sa.getClass() = this.getPyClass()
)
or
exists(FunctionObject method | this.lookupAttribute(_) = method |
attribute_assigned_in_method(method, name) and
b = method.getACall().getBasicBlock()
)
}
pragma[nomagic]
private predicate definedInBlock(BasicBlock b, string name) {
// manual specialisation: this is only called from interestingUndefined,
// so we can push the context in from there, which must apply to a
// SelfAttributeRead in the same scope
exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() |
interestingContext(a, name)
) and
this.definitionInBlock(b, name)
or
exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b)
}
pragma[nomagic]
private predicate definedInBlock(BasicBlock b, string name) {
// manual specialisation: this is only called from interestingUndefined,
// so we can push the context in from there, which must apply to a
// SelfAttributeRead in the same scope
exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() |
interestingContext(a, name)
) and
this.definitionInBlock(b, name)
or
exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b)
}
}
private Object object_getattribute() {
result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__")
result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__")
}
private predicate auto_name(string name) { name = "__class__" or name = "__dict__" }

View File

@@ -14,48 +14,48 @@
import python
predicate does_nothing(PyFunctionObject f) {
not exists(Stmt s | s.getScope() = f.getFunction() |
not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString()
)
not exists(Stmt s | s.getScope() = f.getFunction() |
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 |
meth.getScope() = f.getFunction() and
meth.getFunc() = attr and
attr.getObject() = sup and
attr.getName() = f.getName() and
sup.getFunc() = v.getAnAccess() and
v.getId() = "super"
)
exists(Call sup, Call meth, Attribute attr, GlobalVariable v |
meth.getScope() = f.getFunction() and
meth.getFunc() = attr and
attr.getObject() = sup and
attr.getName() = f.getName() and
sup.getFunc() = v.getAnAccess() and
v.getId() = "super"
)
}
/** Holds if the given name is allowed for some reason */
predicate allowed(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"
name = "process_request"
}
from
ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2
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 allowed(name) and
not o1.overrides(o2) and
not o2.overrides(o1) and
not c.declaresAttribute(name)
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 allowed(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()
o1.toString(), o2, o2.toString()

View File

@@ -15,21 +15,21 @@ import semmle.python.SelfAttribute
import Equality
predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) {
exists(FunctionValue f |
f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name
) and
/* Exclude classes used as metaclasses */
not cls.getASuperType() = ClassValue::type()
exists(FunctionValue f |
f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name
) and
/* Exclude classes used as metaclasses */
not cls.getASuperType() = ClassValue::type()
}
predicate should_override_eq(ClassValue cls, Value base_eq) {
not cls.declaresAttribute("__eq__") and
exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and
not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and
not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and
not base_eq = ClassValue::object().declaredAttribute("__eq__")
)
not cls.declaresAttribute("__eq__") and
exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and
not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and
not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and
not base_eq = ClassValue::object().declaredAttribute("__eq__")
)
}
/**
@@ -37,21 +37,21 @@ predicate should_override_eq(ClassValue cls, Value base_eq) {
* which implies that the __eq__ method does not need to be overridden.
*/
predicate superclassEqExpectsAttribute(ClassValue cls, FunctionValue base_eq, string attrname) {
not cls.declaresAttribute("__eq__") and
exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
exists(SelfAttributeRead store | store.getName() = attrname |
store.getScope() = base_eq.getScope()
)
not cls.declaresAttribute("__eq__") and
exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
exists(SelfAttributeRead store | store.getName() = attrname |
store.getScope() = base_eq.getScope()
)
)
}
from ClassValue cls, SelfAttributeStore store, Value 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())
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()
"The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq,
"'__eq__'", store, store.getName()

View File

@@ -1,14 +1,14 @@
import python
private Attribute dictAccess(LocalVariable var) {
result.getName() = "__dict__" and
result.getObject() = var.getAnAccess()
result.getName() = "__dict__" and
result.getObject() = var.getAnAccess()
}
private Call getattr(LocalVariable obj, LocalVariable attr) {
result.getFunc().(Name).getId() = "getattr" and
result.getArg(0) = obj.getAnAccess() and
result.getArg(1) = attr.getAnAccess()
result.getFunc().(Name).getId() = "getattr" and
result.getArg(0) = obj.getAnAccess() and
result.getArg(1) = attr.getAnAccess()
}
/**
@@ -16,59 +16,59 @@ private Call getattr(LocalVariable obj, LocalVariable attr) {
* or compares attributes using `getattr`.
*/
class GenericEqMethod extends Function {
GenericEqMethod() {
this.getName() = "__eq__" and
exists(LocalVariable self, LocalVariable other |
self.getAnAccess() = this.getArg(0) and
self.getId() = "self" and
other.getAnAccess() = this.getArg(1) and
exists(Compare eq |
eq.getOp(0) instanceof Eq or
eq.getOp(0) instanceof NotEq
|
// `self.__dict__ == other.__dict__`
eq.getAChildNode() = dictAccess(self) and
eq.getAChildNode() = dictAccess(other)
or
// `getattr(self, var) == getattr(other, var)`
exists(Variable var |
eq.getAChildNode() = getattr(self, var) and
eq.getAChildNode() = getattr(other, var)
)
)
GenericEqMethod() {
this.getName() = "__eq__" and
exists(LocalVariable self, LocalVariable other |
self.getAnAccess() = this.getArg(0) and
self.getId() = "self" and
other.getAnAccess() = this.getArg(1) and
exists(Compare eq |
eq.getOp(0) instanceof Eq or
eq.getOp(0) instanceof NotEq
|
// `self.__dict__ == other.__dict__`
eq.getAChildNode() = dictAccess(self) and
eq.getAChildNode() = dictAccess(other)
or
// `getattr(self, var) == getattr(other, var)`
exists(Variable var |
eq.getAChildNode() = getattr(self, var) and
eq.getAChildNode() = getattr(other, var)
)
}
)
)
}
}
/** An `__eq__` method that just does `self is other` */
class IdentityEqMethod extends Function {
IdentityEqMethod() {
this.getName() = "__eq__" and
exists(LocalVariable self, LocalVariable other |
self.getAnAccess() = this.getArg(0) and
self.getId() = "self" and
other.getAnAccess() = this.getArg(1) and
exists(Compare eq | eq.getOp(0) instanceof Is |
eq.getAChildNode() = self.getAnAccess() and
eq.getAChildNode() = other.getAnAccess()
)
)
}
IdentityEqMethod() {
this.getName() = "__eq__" and
exists(LocalVariable self, LocalVariable other |
self.getAnAccess() = this.getArg(0) and
self.getId() = "self" and
other.getAnAccess() = this.getArg(1) and
exists(Compare eq | eq.getOp(0) instanceof Is |
eq.getAChildNode() = self.getAnAccess() and
eq.getAChildNode() = other.getAnAccess()
)
)
}
}
/** An (in)equality method that delegates to its complement */
class DelegatingEqualityMethod extends Function {
DelegatingEqualityMethod() {
exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 |
ret.getScope() = this and
ret.getValue() = not_ and
not_.getOp() instanceof Not and
not_.getOperand() = comp and
comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess())
|
this.getName() = "__eq__" and op instanceof NotEq
or
this.getName() = "__ne__" and op instanceof Eq
)
}
DelegatingEqualityMethod() {
exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 |
ret.getScope() = this and
ret.getValue() = not_ and
not_.getOp() instanceof Not and
not_.getOperand() = comp and
comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess())
|
this.getName() = "__eq__" and op instanceof NotEq
or
this.getName() = "__ne__" and op instanceof Eq
)
}
}

View File

@@ -14,49 +14,49 @@
import python
CallableValue defines_equality(ClassValue c, string name) {
(
name = "__eq__"
or
major_version() = 2 and name = "__cmp__"
) and
result = c.declaredAttribute(name)
(
name = "__eq__"
or
major_version() = 2 and name = "__cmp__"
) and
result = c.declaredAttribute(name)
}
CallableValue implemented_method(ClassValue c, string name) {
result = defines_equality(c, name)
or
result = c.declaredAttribute("__hash__") and name = "__hash__"
result = defines_equality(c, name)
or
result = c.declaredAttribute("__hash__") and name = "__hash__"
}
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
/* 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
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
}
/** Holds if this class is unhashable */
predicate unhashable(ClassValue cls) {
cls.lookup("__hash__") = Value::named("None")
or
cls.lookup("__hash__").(CallableValue).neverReturns()
cls.lookup("__hash__") = Value::named("None")
or
cls.lookup("__hash__").(CallableValue).neverReturns()
}
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
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()
c.getName()

View File

@@ -16,33 +16,33 @@ import Equality
string equals_or_ne() { result = "__eq__" or result = "__ne__" }
predicate total_ordering(Class cls) {
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
or
exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering")
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
or
exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering")
}
CallableValue implemented_method(ClassValue c, string name) {
result = c.declaredAttribute(name) and name = equals_or_ne()
result = c.declaredAttribute(name) and name = equals_or_ne()
}
string unimplemented_method(ClassValue c) {
not c.declaresAttribute(result) and result = equals_or_ne()
not c.declaresAttribute(result) and result = equals_or_ne()
}
predicate violates_equality_contract(
ClassValue c, string present, string missing, CallableValue method
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
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()
c.getName()

View File

@@ -13,61 +13,61 @@
import python
predicate total_ordering(Class cls) {
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
or
exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering")
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
or
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 = "__ge__" and n = 4
result = "__lt__" and n = 1
or
result = "__le__" and n = 2
or
result = "__gt__" and n = 3
or
result = "__ge__" and n = 4
}
predicate overrides_ordering_method(ClassValue c, string name) {
name = ordering_name(_) and
(
c.declaresAttribute(name)
or
exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") |
sup.declaresAttribute(name)
)
name = ordering_name(_) and
(
c.declaresAttribute(name)
or
exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") |
sup.declaresAttribute(name)
)
)
}
string unimplemented_ordering(ClassValue c, int n) {
not c = Value::named("object") and
not overrides_ordering_method(c, result) and
result = ordering_name(n)
not c = Value::named("object") 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, _))
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) |
prefix = "" and result = unimplemented_ordering(c, n)
or
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
or
prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n)
)
result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5
or
prefix != "" and result = prefix + " or " + unimplemented_ordering(c, 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)
/* 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)
}
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, _))
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
"Class " + c.getName() + " implements $@, but does not implement " +
unimplemented_ordering_methods(c, 4) + ".", ordering, name

View File

@@ -13,18 +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()
t.isNewStyle() and
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()
"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

@@ -14,17 +14,17 @@
import python
from
ClassObject supercls, string method, Call call, FunctionObject overriding,
FunctionObject overridden
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)
)
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()
overriding, overriding.descriptiveString()

View File

@@ -15,30 +15,30 @@ import semmle.python.SelfAttribute
import ClassAttributes
predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) {
c.sometimesDefines(a.getName()) and
exists(SelfAttributeRead guard, If i |
i.contains(a) and
c.assignedInInit(guard.getName())
|
i.getTest() = guard
or
i.getTest().contains(guard)
)
c.sometimesDefines(a.getName()) and
exists(SelfAttributeRead guard, If i |
i.contains(a) and
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
not guarded_by_other_attribute(a, c)
c.sometimesDefines(a.getName()) and
not c.alwaysDefines(a.getName()) 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()
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"
"Attribute '" + a.getName() +
"' is not defined in the class body nor in the __init__() method, but it is defined $@", sa,
"here"

View File

@@ -3,73 +3,73 @@ import python
// Helper predicates for multiple call to __init__/__del__ queries.
pragma[noinline]
private predicate multiple_invocation_paths_helper(
FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi
FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi
) {
i1 != i2 and
i1 = top.getACallee+() and
i2 = top.getACallee+() and
i1.getFunction() = multi
i1 != i2 and
i1 = top.getACallee+() and
i2 = top.getACallee+() and
i1.getFunction() = multi
}
pragma[noinline]
private predicate multiple_invocation_paths(
FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi
FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi
) {
multiple_invocation_paths_helper(top, i1, i2, multi) and
i2.getFunction() = multi
multiple_invocation_paths_helper(top, i1, i2, multi) and
i2.getFunction() = multi
}
/** Holds if `self.name` calls `multi` by multiple paths, and thus calls it more than once. */
predicate multiple_calls_to_superclass_method(ClassObject self, FunctionObject multi, string name) {
exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 |
multiple_invocation_paths(top, i1, i2, multi) and
top.runtime(self.declaredAttribute(name)) and
self.getASuperType().declaredAttribute(name) = multi
|
// Only called twice if called from different functions,
// or if one call-site can reach the other.
i1.getCall().getScope() != i2.getCall().getScope()
or
i1.getCall().strictlyReaches(i2.getCall())
)
exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 |
multiple_invocation_paths(top, i1, i2, multi) and
top.runtime(self.declaredAttribute(name)) and
self.getASuperType().declaredAttribute(name) = multi
|
// Only called twice if called from different functions,
// or if one call-site can reach the other.
i1.getCall().getScope() != i2.getCall().getScope()
or
i1.getCall().strictlyReaches(i2.getCall())
)
}
/** Holds if all attributes called `name` can be inferred to be methods. */
private predicate named_attributes_not_method(ClassObject cls, string name) {
cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject
cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject
}
/** Holds if `f` actually does something. */
private predicate does_something(FunctionObject f) {
f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__")
or
exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass)
f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__")
or
exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass)
}
/** Holds if `meth` looks like it should have a call to `name`, but does not */
private predicate missing_call(FunctionObject meth, string name) {
exists(CallNode call, AttrNode attr |
call.getScope() = meth.getFunction() and
call.getFunction() = attr and
attr.getName() = name and
not exists(FunctionObject f | f.getACall() = call)
)
exists(CallNode call, AttrNode attr |
call.getScope() = meth.getFunction() and
call.getFunction() = attr and
attr.getName() = name and
not exists(FunctionObject f | f.getACall() = call)
)
}
/** Holds if `self.name` does not call `missing`, even though it is expected to. */
predicate missing_call_to_superclass_method(
ClassObject self, FunctionObject top, FunctionObject missing, string name
ClassObject self, FunctionObject top, FunctionObject missing, string name
) {
missing = self.getASuperType().declaredAttribute(name) and
top = self.lookupAttribute(name) and
/* There is no call to missing originating from top */
not top.getACallee*() = missing and
/* Make sure that all named 'methods' are objects that we can understand. */
not exists(ClassObject sup |
sup = self.getAnImproperSuperType() and
named_attributes_not_method(sup, name)
) and
not self.isAbstract() and
does_something(missing) and
not missing_call(top, name)
missing = self.getASuperType().declaredAttribute(name) and
top = self.lookupAttribute(name) and
/* There is no call to missing originating from top */
not top.getACallee*() = missing and
/* Make sure that all named 'methods' are objects that we can understand. */
not exists(ClassObject sup |
sup = self.getAnImproperSuperType() and
named_attributes_not_method(sup, name)
) and
not self.isAbstract() and
does_something(missing) and
not missing_call(top, name)
}

View File

@@ -15,10 +15,10 @@ 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()
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()
"Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.",
missing, missing.descriptiveString()

View File

@@ -15,14 +15,14 @@ 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
// If a superclass is incorrect, don't flag this class as well.
not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and
not missing.neverReturns() and
not self.failedInference() and
not missing.isBuiltin() and
not self.isAbstract()
self.lookupAttribute("__init__") = initializer and
missing_call_to_superclass_method(self, initializer, missing, "__init__") and
// If a superclass is incorrect, don't flag this class as well.
not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and
not missing.neverReturns() and
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"
"Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.",
missing, missing.descriptiveString(), initializer, "__init__ method"

View File

@@ -13,20 +13,20 @@
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 |
name = "__get__" or name = "__set__" or name = "__delete__"
) and
cls.lookupAttribute(_) = f and
get_set.getACallee*() = f and
not f.getName() = "__init__" and
s.getScope() = f.getFunction()
)
cls.isDescriptorType() and
exists(PyFunctionObject f, PyFunctionObject get_set |
exists(string name | cls.lookupAttribute(name) = get_set |
name = "__get__" or name = "__set__" or name = "__delete__"
) and
cls.lookupAttribute(_) = f and
get_set.getACallee*() = f and
not f.getName() = "__init__" and
s.getScope() = f.getFunction()
)
}
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()
"Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.",
cls, cls.getName()

View File

@@ -14,79 +14,79 @@
import python
class InitCallStmt extends ExprStmt {
InitCallStmt() {
exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() |
attr.getName() = "__init__"
)
}
InitCallStmt() {
exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() |
attr.getName() = "__init__"
)
}
}
predicate overwrites_which(Function subinit, AssignStmt write_attr, string which) {
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"
)
)
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"
)
)
)
}
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
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)
)
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
"Assignment overwrites attribute " + name + ", which was previously defined in " + classtype +
" $@.", overwritten, classname

View File

@@ -15,5 +15,5 @@ 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."
"Property " + prop.getName() + " will not work properly, as class " + cls.getName() +
" is an old-style class."

View File

@@ -17,5 +17,5 @@ import python
from ClassValue c
where not c.isBuiltin() 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."
"Class " + c.getName() +
" implements __del__ (presumably to release some resource). Consider making it a context manager."

View File

@@ -20,27 +20,27 @@
import python
predicate shadowed_by_super_class(
ClassObject c, ClassObject supercls, Assign assign, FunctionObject f
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
attr.getObject().(Name).getId() = "self" and
attr.getName() = f.getName() and
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.
*/
c.getASuperType() = supercls and
c.declaredAttribute(_) = f and
exists(FunctionObject init, Attribute attr |
supercls.declaredAttribute("__init__") = init and
attr = assign.getATarget() and
attr.getObject().(Name).getId() = "self" and
attr.getName() = f.getName() and
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.
*/
not supercls.hasAttribute(f.getName())
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"
"Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.",
assign, "an attribute"

View File

@@ -13,13 +13,13 @@
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
s.getFunc().(Name).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

View File

@@ -15,14 +15,14 @@ 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()
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()
"Class " + self.getName() +
" may not be cleaned up properly as $@ may be called multiple times during destruction.", multi,
multi.descriptiveString()

View File

@@ -15,15 +15,15 @@ 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()
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()
"Class " + self.getName() +
" may not be initialized properly as $@ may be called multiple times during initialization.",
multi, multi.descriptiveString()

View File

@@ -15,18 +15,18 @@ import semmle.python.SelfAttribute
import ClassAttributes
predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) {
name = a.getName() and
not c.sometimesDefines(name) and
c.interestingUndefined(a) and
line = a.getLocation().getStartLine() and
not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name)
name = a.getName() and
not c.sometimesDefines(name) and
c.interestingUndefined(a) and
line = a.getLocation().getStartLine() and
not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name)
}
predicate report_undefined_class_attribute(Attribute a, ClassObject c, string name) {
exists(int line |
undefined_class_attribute(a, c, line, name) and
line = min(int x | undefined_class_attribute(_, c, x, name))
)
exists(int line |
undefined_class_attribute(a, c, line, name) and
line = min(int x | undefined_class_attribute(_, c, x, name))
)
}
from Attribute a, ClassObject c, string name

View File

@@ -13,76 +13,76 @@
import python
predicate fewer_than_two_public_methods(Class cls, int methods) {
(methods = 0 or methods = 1) and
methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod())
(methods = 0 or methods = 1) and
methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod())
}
predicate does_not_define_special_method(Class cls) {
not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod())
not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod())
}
predicate no_inheritance(Class c) {
not exists(ClassValue cls, ClassValue other |
cls.getScope() = c and
other != ClassValue::object()
|
other.getABaseType() = cls or
cls.getABaseType() = other
) and
not exists(Expr base | base = c.getABase() |
not base instanceof Name or base.(Name).getId() != "object"
)
not exists(ClassValue cls, ClassValue other |
cls.getScope() = c and
other != ClassValue::object()
|
other.getABaseType() = cls or
cls.getABaseType() = other
) and
not exists(Expr base | base = c.getABase() |
not base instanceof Name or base.(Name).getId() != "object"
)
}
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(Subscript s | s.getScope() = method and s.getCtx() = ctx)
or
exists(Attribute a | a.getScope() = method and a.getCtx() = ctx)
)
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(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(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"
)
}
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
not is_stateful(c)
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."
)
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

@@ -18,7 +18,7 @@ import Expressions.CallArgs
from Call call, ClassValue cls, string name, FunctionValue init
where
illegally_named_parameter(call, cls, name) and
init = get_function_or_initializer(cls)
illegally_named_parameter(call, cls, name) and
init = get_function_or_initializer(cls)
select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init,
init.getQualifiedName()
init.getQualifiedName()

View File

@@ -17,15 +17,15 @@ import Expressions.CallArgs
from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init
where
(
too_many_args(call, cls, limit) and
too = "too many arguments" and
should = "no more than "
or
too_few_args(call, cls, limit) and
too = "too few arguments" and
should = "no fewer than "
) and
init = get_function_or_initializer(cls)
(
too_many_args(call, cls, limit) and
too = "too many arguments" and
should = "no more than "
or
too_few_args(call, cls, limit) and
too = "too few arguments" and
should = "no fewer than "
) and
init = get_function_or_initializer(cls)
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init,
init.getQualifiedName()
init.getQualifiedName()

View File

@@ -17,13 +17,13 @@ import python
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)
catches_base_exception(ex) and
doesnt_reraise(ex)
select ex, "Except block directly handles BaseException."

View File

@@ -14,88 +14,88 @@
import python
predicate empty_except(ExceptStmt ex) {
not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass)
not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass)
}
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) {
ex.getType().pointsTo(ClassValue::stopIteration())
ex.getType().pointsTo(ClassValue::stopIteration())
}
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
|
/* 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 */
pred.getNode().getParentNode*() = try.getAStmt() and
not succ.getNode().getParentNode*() = try.getAStmt()
)
exists(ControlFlowNode pred, ControlFlowNode succ |
/* Exists a non-exception predecessor, successor pair */
pred.getASuccessor() = succ and
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 */
pred.getNode().getParentNode*() = try.getAStmt() and
not succ.getNode().getParentNode*() = try.getAStmt()
)
}
predicate attribute_access(Stmt s) {
s.(ExprStmt).getValue() instanceof Attribute
or
exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name |
name = "getattr" or name = "setattr" or name = "delattr"
)
or
s.(Delete).getATarget() instanceof Attribute
s.(ExprStmt).getValue() instanceof Attribute
or
exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name |
name = "getattr" or name = "setattr" or name = "delattr"
)
or
s.(Delete).getATarget() instanceof Attribute
}
predicate subscript(Stmt s) {
s.(ExprStmt).getValue() instanceof Subscript
or
s.(Delete).getATarget() instanceof Subscript
s.(ExprStmt).getValue() instanceof Subscript
or
s.(Delete).getATarget() instanceof Subscript
}
predicate encode_decode(Call ex, ClassValue type) {
exists(string name | ex.getFunc().(Attribute).getName() = name |
name = "encode" and type = ClassValue::unicodeEncodeError()
or
name = "decode" and type = ClassValue::unicodeDecodeError()
)
exists(string name | ex.getFunc().(Attribute).getName() = name |
name = "encode" and type = ClassValue::unicodeEncodeError()
or
name = "decode" and type = ClassValue::unicodeDecodeError()
)
}
predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) {
not exists(ex.getTry().getStmt(1)) and
s = ex.getTry().getStmt(0) and
ex.getType().pointsTo(type)
not exists(ex.getTry().getStmt(1)) and
s = ex.getTry().getStmt(0) and
ex.getType().pointsTo(type)
}
predicate focussed_handler(ExceptStmt ex) {
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()
or
s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError()
or
encode_decode(s.(ExprStmt).getValue(), 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()
or
s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError()
or
encode_decode(s.(ExprStmt).getValue(), type)
)
}
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)
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

@@ -15,16 +15,16 @@ import python
from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what
where
ex.handledException(t, c, origin) and
(
exists(ClassValue x | x = t |
not x.isLegalExceptionType() and
not x.failedInference(_) and
what = "class '" + x.getName() + "'"
)
or
not t instanceof ClassValue and
what = "instance of '" + c.getName() + "'"
ex.handledException(t, c, origin) and
(
exists(ClassValue x | x = t |
not x.isLegalExceptionType() and
not x.failedInference(_) and
what = "class '" + x.getName() + "'"
)
or
not t instanceof ClassValue and
what = "instance of '" + c.getName() + "'"
)
select ex.getNode(),
"Non-exception $@ in exception handler which will never match raised exception.", origin, what
"Non-exception $@ in exception handler which will never match raised exception.", origin, what

View File

@@ -17,9 +17,9 @@ 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, _)
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."
"Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead."

View File

@@ -15,14 +15,14 @@
import python
predicate incorrect_except_order(ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2) {
exists(int i, int j, Try t |
ex1 = t.getHandler(i) and
ex2 = t.getHandler(j) and
i < j and
cls1 = except_class(ex1) and
cls2 = except_class(ex2) and
cls1 = cls2.getASuperType()
)
exists(int i, int j, Try t |
ex1 = t.getHandler(i) and
ex2 = t.getHandler(j) and
i < j and
cls1 = except_class(ex1) and
cls2 = except_class(ex2) and
cls1 = cls2.getASuperType()
)
}
ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) }
@@ -30,5 +30,5 @@ ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) }
from ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2
where incorrect_except_order(ex1, cls1, ex2, cls2)
select ex2,
"Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.",
cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName()
"Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.",
cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName()

View File

@@ -2,9 +2,9 @@ import python
/** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */
predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) {
notimpl.pointsTo(Value::named("NotImplemented")) and
(
notimpl = raise.getException() or
notimpl = raise.getException().(Call).getFunc()
)
notimpl.pointsTo(Value::named("NotImplemented")) and
(
notimpl = raise.getException() or
notimpl = raise.getException().(Call).getFunc()
)
}

View File

@@ -2,11 +2,11 @@ import python
/** Whether the raise statement 'r' raises 'type' from origin 'orig' */
predicate type_or_typeof(Raise r, ClassValue type, AstNode orig) {
exists(Expr exception | exception = r.getRaised() |
exception.pointsTo(type, orig)
or
not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and
not type = ClassValue::type() and // First value is an unknown exception type
exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type)
)
exists(Expr exception | exception = r.getRaised() |
exception.pointsTo(type, orig)
or
not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and
not type = ClassValue::type() and // First value is an unknown exception type
exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type)
)
}

View File

@@ -13,10 +13,10 @@ import python
from Raise r, Value v, AstNode origin
where
r.getException().pointsTo(v, origin) and
v.getClass() = ClassValue::tuple() and
major_version() = 2
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"
"Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.",
origin, "a tuple"

View File

@@ -17,45 +17,45 @@ FunctionValue iter() { result = Value::named("iter") }
BuiltinFunctionValue next() { result = Value::named("next") }
predicate call_to_iter(CallNode call, EssaVariable sequence) {
sequence.getAUse() = iter().getArgumentForCall(call, 0)
sequence.getAUse() = iter().getArgumentForCall(call, 0)
}
predicate call_to_next(CallNode call, ControlFlowNode iter) {
iter = next().getArgumentForCall(call, 0)
iter = next().getArgumentForCall(call, 0)
}
predicate call_to_next_has_default(CallNode call) {
exists(call.getArg(1)) or exists(call.getArgByName("default"))
exists(call.getArg(1)) or exists(call.getArgByName("default"))
}
predicate guarded_not_empty_sequence(EssaVariable sequence) {
sequence.getDefinition() instanceof EssaEdgeRefinement
sequence.getDefinition() instanceof EssaEdgeRefinement
}
/** The pattern `next(iter(x))` is often used where `x` is known not be empty. Check for that. */
predicate iter_not_exhausted(EssaVariable iterator) {
exists(EssaVariable sequence |
call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and
guarded_not_empty_sequence(sequence)
)
exists(EssaVariable sequence |
call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and
guarded_not_empty_sequence(sequence)
)
}
predicate stop_iteration_handled(CallNode call) {
exists(Try t |
t.containsInScope(call.getNode()) and
t.getAHandler().getType().pointsTo(ClassValue::stopIteration())
)
exists(Try t |
t.containsInScope(call.getNode()) and
t.getAHandler().getType().pointsTo(ClassValue::stopIteration())
)
}
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)
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

@@ -1,36 +1,36 @@
/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */
/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */
import python
import Testing.Mox
private int varargs_length_objectapi(Call call) {
not exists(call.getStarargs()) and result = 0
or
exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength())
or
result = count(call.getStarargs().(List).getAnElt())
not exists(call.getStarargs()) and result = 0
or
exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength())
or
result = count(call.getStarargs().(List).getAnElt())
}
private int varargs_length(Call call) {
not exists(call.getStarargs()) and result = 0
or
exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length())
or
result = count(call.getStarargs().(List).getAnElt())
not exists(call.getStarargs()) and result = 0
or
exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length())
or
result = count(call.getStarargs().(List).getAnElt())
}
/** Gets a keyword argument that is not a keyword-only parameter. */
private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) {
func.getACall().getNode() = call and
result = call.getAKeyword() and
not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg()
func.getACall().getNode() = call and
result = call.getAKeyword() and
not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg()
}
/** Gets a keyword argument that is not a keyword-only parameter. */
private Keyword not_keyword_only_arg(Call call, FunctionValue func) {
func.getACall().getNode() = call and
result = call.getAKeyword() and
not func.getScope().getAKeywordOnlyArg().getId() = result.getArg()
func.getACall().getNode() = call and
result = call.getAKeyword() and
not func.getScope().getAKeywordOnlyArg().getId() = result.getArg()
}
/**
@@ -40,17 +40,17 @@ private Keyword not_keyword_only_arg(Call call, FunctionValue func) {
* plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs).
*/
private int positional_arg_count_for_call_objectapi(Call call, Object callable) {
call = get_a_call_objectapi(callable).getNode() and
exists(int positional_keywords |
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
not func.getFunction().hasKwArg() and
positional_keywords = count(not_keyword_only_arg_objectapi(call, func))
or
func.getFunction().hasKwArg() and positional_keywords = 0
)
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
call = get_a_call_objectapi(callable).getNode() and
exists(int positional_keywords |
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
not func.getFunction().hasKwArg() and
positional_keywords = count(not_keyword_only_arg_objectapi(call, func))
or
func.getFunction().hasKwArg() and positional_keywords = 0
)
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
)
}
/**
@@ -60,174 +60,174 @@ private int positional_arg_count_for_call_objectapi(Call call, Object callable)
* plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs).
*/
private int positional_arg_count_for_call(Call call, Value callable) {
call = get_a_call(callable).getNode() and
exists(int positional_keywords |
exists(FunctionValue func | func = get_function_or_initializer(callable) |
not func.getScope().hasKwArg() and
positional_keywords = count(not_keyword_only_arg(call, func))
or
func.getScope().hasKwArg() and positional_keywords = 0
)
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
call = get_a_call(callable).getNode() and
exists(int positional_keywords |
exists(FunctionValue func | func = get_function_or_initializer(callable) |
not func.getScope().hasKwArg() and
positional_keywords = count(not_keyword_only_arg(call, func))
or
func.getScope().hasKwArg() and positional_keywords = 0
)
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
)
}
/** Gets the number of arguments in `call`. */
int arg_count_objectapi(Call call) {
result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword())
result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword())
}
/** Gets the number of arguments in `call`. */
int arg_count(Call call) {
result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword())
result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword())
}
/** Gets a call corresponding to the given class or function. */
private ControlFlowNode get_a_call_objectapi(Object callable) {
result = callable.(ClassObject).getACall()
or
result = callable.(FunctionObject).getACall()
result = callable.(ClassObject).getACall()
or
result = callable.(FunctionObject).getACall()
}
/** Gets a call corresponding to the given class or function. */
private ControlFlowNode get_a_call(Value callable) {
result = callable.(ClassValue).getACall()
or
result = callable.(FunctionValue).getACall()
result = callable.(ClassValue).getACall()
or
result = callable.(FunctionValue).getACall()
}
/** Gets the function object corresponding to the given class or function. */
FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) {
result = func_or_cls.(FunctionObject)
or
result = func_or_cls.(ClassObject).declaredAttribute("__init__")
result = func_or_cls.(FunctionObject)
or
result = func_or_cls.(ClassObject).declaredAttribute("__init__")
}
/** Gets the function object corresponding to the given class or function. */
FunctionValue get_function_or_initializer(Value func_or_cls) {
result = func_or_cls.(FunctionValue)
or
result = func_or_cls.(ClassValue).declaredAttribute("__init__")
result = func_or_cls.(FunctionValue)
or
result = func_or_cls.(ClassValue).declaredAttribute("__init__")
}
/**Whether there is an illegally named parameter called `name` in the `call` to `func` */
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
not func.isC() and
name = call.getANamedArgumentName() and
call.getAFlowNode() = get_a_call_objectapi(func) and
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
not func.isC() and
name = call.getANamedArgumentName() and
call.getAFlowNode() = get_a_call_objectapi(func) and
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
}
/**Whether there is an illegally named parameter called `name` in the `call` to `func` */
predicate illegally_named_parameter(Call call, Value func, string name) {
not func.isBuiltin() and
name = call.getANamedArgumentName() and
call.getAFlowNode() = get_a_call(func) and
not get_function_or_initializer(func).isLegalArgumentName(name)
not func.isBuiltin() and
name = call.getANamedArgumentName() and
call.getAFlowNode() = get_a_call(func) and
not get_function_or_initializer(func).isLegalArgumentName(name)
}
/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */
predicate too_few_args_objectapi(Call call, Object callable, int limit) {
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter_objectapi(call, callable, _) and
not exists(call.getStarargs()) and
not exists(call.getKwargs()) and
arg_count_objectapi(call) < limit and
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
call = func.getAFunctionCall().getNode() and
limit = func.minParameters() and
// The combination of misuse of `mox.Mox().StubOutWithMock()`
// and a bug in mox's implementation of methods results in having to
// pass 1 too few arguments to the mocked function.
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
or
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
or
callable instanceof ClassObject and
call.getAFlowNode() = get_a_call_objectapi(callable) and
limit = func.minParameters() - 1
)
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter_objectapi(call, callable, _) and
not exists(call.getStarargs()) and
not exists(call.getKwargs()) and
arg_count_objectapi(call) < limit and
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
call = func.getAFunctionCall().getNode() and
limit = func.minParameters() and
// The combination of misuse of `mox.Mox().StubOutWithMock()`
// and a bug in mox's implementation of methods results in having to
// pass 1 too few arguments to the mocked function.
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
or
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
or
callable instanceof ClassObject and
call.getAFlowNode() = get_a_call_objectapi(callable) and
limit = func.minParameters() - 1
)
}
/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */
predicate too_few_args(Call call, Value callable, int limit) {
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter(call, callable, _) and
not exists(call.getStarargs()) and
not exists(call.getKwargs()) and
arg_count(call) < limit and
exists(FunctionValue func | func = get_function_or_initializer(callable) |
call = func.getAFunctionCall().getNode() and
limit = func.minParameters() and
/*
* The combination of misuse of `mox.Mox().StubOutWithMock()`
* and a bug in mox's implementation of methods results in having to
* pass 1 too few arguments to the mocked function.
*/
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter(call, callable, _) and
not exists(call.getStarargs()) and
not exists(call.getKwargs()) and
arg_count(call) < limit and
exists(FunctionValue func | func = get_function_or_initializer(callable) |
call = func.getAFunctionCall().getNode() and
limit = func.minParameters() and
/*
* The combination of misuse of `mox.Mox().StubOutWithMock()`
* and a bug in mox's implementation of methods results in having to
* pass 1 too few arguments to the mocked function.
*/
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
or
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
or
callable instanceof ClassValue and
call.getAFlowNode() = get_a_call(callable) and
limit = func.minParameters() - 1
)
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
or
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
or
callable instanceof ClassValue and
call.getAFlowNode() = get_a_call(callable) and
limit = func.minParameters() - 1
)
}
/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */
predicate too_many_args_objectapi(Call call, Object callable, int limit) {
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter_objectapi(call, callable, _) and
exists(FunctionObject func |
func = get_function_or_initializer_objectapi(callable) and
not func.getFunction().hasVarArg() and
limit >= 0
|
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
or
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
or
callable instanceof ClassObject and
call.getAFlowNode() = get_a_call_objectapi(callable) and
limit = func.maxParameters() - 1
) and
positional_arg_count_for_call_objectapi(call, callable) > limit
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter_objectapi(call, callable, _) and
exists(FunctionObject func |
func = get_function_or_initializer_objectapi(callable) and
not func.getFunction().hasVarArg() and
limit >= 0
|
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
or
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
or
callable instanceof ClassObject and
call.getAFlowNode() = get_a_call_objectapi(callable) and
limit = func.maxParameters() - 1
) and
positional_arg_count_for_call_objectapi(call, callable) > limit
}
/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */
predicate too_many_args(Call call, Value callable, int limit) {
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter(call, callable, _) and
exists(FunctionValue func |
func = get_function_or_initializer(callable) and
not func.getScope().hasVarArg() and
limit >= 0
|
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
or
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
or
callable instanceof ClassValue and
call.getAFlowNode() = get_a_call(callable) and
limit = func.maxParameters() - 1
) and
positional_arg_count_for_call(call, callable) > limit
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
not illegally_named_parameter(call, callable, _) and
exists(FunctionValue func |
func = get_function_or_initializer(callable) and
not func.getScope().hasVarArg() and
limit >= 0
|
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
or
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
or
callable instanceof ClassValue and
call.getAFlowNode() = get_a_call(callable) and
limit = func.maxParameters() - 1
) and
positional_arg_count_for_call(call, callable) > limit
}
/** Holds if `call` has too many or too few arguments for `func` */
predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string too) {
too_few_args_objectapi(call, func, limit) and too = "too few"
or
too_many_args_objectapi(call, func, limit) and too = "too many"
too_few_args_objectapi(call, func, limit) and too = "too few"
or
too_many_args_objectapi(call, func, limit) and too = "too many"
}
/** Holds if `call` has too many or too few arguments for `func` */
predicate wrong_args(Call call, FunctionValue func, int limit, string too) {
too_few_args(call, func, limit) and too = "too few"
or
too_many_args(call, func, limit) and too = "too many"
too_few_args(call, func, limit) and too = "too few"
or
too_many_args(call, func, limit) and too = "too many"
}
/**
@@ -236,8 +236,8 @@ predicate wrong_args(Call call, FunctionValue func, int limit, string too) {
*/
bindingset[call, func]
predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject func) {
arg_count_objectapi(call) + 1 >= func.minParameters() and
arg_count_objectapi(call) < func.maxParameters()
arg_count_objectapi(call) + 1 >= func.minParameters() and
arg_count_objectapi(call) < func.maxParameters()
}
/**
@@ -246,23 +246,23 @@ predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject f
*/
bindingset[call, func]
predicate correct_args_if_called_as_method(Call call, FunctionValue func) {
arg_count(call) + 1 >= func.minParameters() and
arg_count(call) < func.maxParameters()
arg_count(call) + 1 >= func.minParameters() and
arg_count(call) < func.maxParameters()
}
/** Holds if `call` is a call to `overriding`, which overrides `func`. */
predicate overridden_call_objectapi(FunctionObject func, FunctionObject overriding, Call call) {
overriding.overrides(func) and
overriding.getACall().getNode() = call
overriding.overrides(func) and
overriding.getACall().getNode() = call
}
/** Holds if `call` is a call to `overriding`, which overrides `func`. */
predicate overridden_call(FunctionValue func, FunctionValue overriding, Call call) {
overriding.overrides(func) and
overriding.getACall().getNode() = call
overriding.overrides(func) and
overriding.getACall().getNode() = call
}
/** Holds if `func` will raise a `NotImplemented` error. */
predicate isAbstract(FunctionValue func) {
func.getARaisedType() = ClassValue::notImplementedError()
func.getARaisedType() = ClassValue::notImplementedError()
}

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,8 +16,8 @@ import python
from Compare comparison, Expr left, Expr right
where
comparison.compares(left, _, right) and
left.isConstant() and
right.isConstant() and
not exists(Assert a | a.getTest() = comparison)
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

@@ -21,9 +21,9 @@ import semmle.python.Comparisons
*/
private predicate is_complex(Expr comp) {
exists(comp.(Compare).getOp(1))
or
is_complex(comp.(UnaryExpr).getOperand())
exists(comp.(Compare).getOp(1))
or
is_complex(comp.(UnaryExpr).getOperand())
}
/**
@@ -31,21 +31,21 @@ private predicate is_complex(Expr comp) {
* strict and also controls that block.
*/
private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) {
controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and
/* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */
not is_complex(controls.getTest().getNode())
controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and
/* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */
not is_complex(controls.getTest().getNode())
}
private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) {
forex(Comparison compnode, ConditionBlock block |
compnode.getNode() = comp and
block.getLastNode().getNode() = previous
|
useless_test(compnode, block, isTrue)
)
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, _)
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

@@ -14,21 +14,21 @@ import python
import semmle.python.pointsto.PointsTo
predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) {
exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() |
op instanceof In or op instanceof NotIn
)
exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() |
op instanceof In or op instanceof NotIn
)
}
from ControlFlowNode non_seq, Compare cmp, Value v, ClassValue cls, ControlFlowNode origin
where
rhs_in_expr(non_seq, cmp) and
non_seq.pointsTo(_, v, origin) and
v.getClass() = cls and
not Types::failedInference(cls, _) and
not cls.hasAttribute("__contains__") and
not cls.hasAttribute("__iter__") and
not cls.hasAttribute("__getitem__") and
not cls = ClassValue::nonetype() and
not cls = Value::named("types.MappingProxyType")
rhs_in_expr(non_seq, cmp) and
non_seq.pointsTo(_, v, origin) and
v.getClass() = cls and
not Types::failedInference(cls, _) and
not cls.hasAttribute("__contains__") and
not cls.hasAttribute("__iter__") and
not cls.hasAttribute("__getitem__") and
not cls = ClassValue::nonetype() and
not cls = Value::named("types.MappingProxyType")
select cmp, "This test may raise an Exception as the $@ may be of non-container class $@.", origin,
"target", cls, cls.getName()
"target", cls, cls.getName()

View File

@@ -15,31 +15,31 @@ import python
import semmle.python.strings
predicate dict_key(Dict d, Expr k, string s) {
k = d.getAKey() and
(
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 |
s = "u\"" + c.getText() + "\"" and c.isUnicode()
or
s = "b\"" + c.getText() + "\"" and not c.isUnicode()
)
k = d.getAKey() and
(
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 |
s = "u\"" + c.getText() + "\"" and c.isUnicode()
or
s = "b\"" + c.getText() + "\"" and not c.isUnicode()
)
)
}
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())
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

@@ -15,12 +15,12 @@ import semmle.python.strings
from Expr e, ClassValue t
where
exists(BinaryExpr b |
b.getOp() instanceof Mod and
format_string(b.getLeft()) and
e = b.getRight() and
mapping_format(b.getLeft()) and
e.pointsTo().getClass() = t and
not t.isMapping()
)
exists(BinaryExpr b |
b.getOp() instanceof Mod and
format_string(b.getLeft()) and
e = b.getRight() and
mapping_format(b.getLeft()) and
e.pointsTo().getClass() = t and
not t.isMapping()
)
select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName()

View File

@@ -13,20 +13,20 @@
import python
class DelCall extends Call {
DelCall() { this.getFunc().(Attribute).getName() = "__del__" }
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"
)
)
}
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

View File

@@ -2,125 +2,125 @@ import python
/** A string constant that looks like it may be used in string formatting operations. */
library class PossibleAdvancedFormatString extends StrConst {
PossibleAdvancedFormatString() { this.getText().matches("%{%}%") }
PossibleAdvancedFormatString() { this.getText().matches("%{%}%") }
private predicate field(int start, int end) {
brace_pair(this, start, end) and
this.getText().substring(start, end) != "{{}}"
}
private predicate field(int start, int end) {
brace_pair(this, start, end) and
this.getText().substring(start, end) != "{{}}"
}
/** Gets the number of the formatting field at [start, end) */
int getFieldNumber(int start, int end) {
result = this.fieldId(start, end).toInt()
or
this.implicitlyNumberedField(start, end) and
result = count(int s | this.implicitlyNumberedField(s, _) and s < start)
}
/** Gets the number of the formatting field at [start, end) */
int getFieldNumber(int start, int end) {
result = this.fieldId(start, end).toInt()
or
this.implicitlyNumberedField(start, end) and
result = count(int s | this.implicitlyNumberedField(s, _) and s < start)
}
/** Gets the text of the formatting field at [start, end) */
string getField(int start, int end) {
this.field(start, end) and
result = this.getText().substring(start, end)
}
/** Gets the text of the formatting field at [start, end) */
string getField(int start, int end) {
this.field(start, end) and
result = this.getText().substring(start, end)
}
private string fieldId(int start, int end) {
this.field(start, end) and
(
result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1)
or
result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+")
)
}
private string fieldId(int start, int end) {
this.field(start, end) and
(
result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1)
or
result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+")
)
}
/** Gets the name of the formatting field at [start, end) */
string getFieldName(int start, int end) {
result = this.fieldId(start, end) and
not exists(this.getFieldNumber(start, end))
}
/** Gets the name of the formatting field at [start, end) */
string getFieldName(int start, int end) {
result = this.fieldId(start, end) and
not exists(this.getFieldNumber(start, end))
}
private predicate implicitlyNumberedField(int start, int end) {
this.field(start, end) and
exists(string c | start + 1 = this.getText().indexOf(c) |
c = "}" or c = ":" or c = "!" or c = "."
)
}
private predicate implicitlyNumberedField(int start, int end) {
this.field(start, end) and
exists(string c | start + 1 = this.getText().indexOf(c) |
c = "}" or c = ":" or c = "!" or c = "."
)
}
/** Whether this format string has implicitly numbered fields */
predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) }
/** Whether this format string has implicitly numbered fields */
predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) }
/** Whether this format string has explicitly numbered fields */
predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) }
/** Whether this format string has explicitly numbered fields */
predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) }
}
/** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */
predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
exists(string text | text = fmt.getText() |
text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1
or
text.charAt(index) = "{" and
text.charAt(index - 1) = "{" and
brace_sequence(fmt, index - 1, len - 1)
)
exists(string text | text = fmt.getText() |
text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1
or
text.charAt(index) = "{" and
text.charAt(index - 1) = "{" and
brace_sequence(fmt, index - 1, len - 1)
)
}
/** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */
predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) {
exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0)
exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0)
}
/** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */
predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) {
escaped_brace(fmt, index + 1)
escaped_brace(fmt, index + 1)
}
private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) {
not escaping_brace(fmt, start) and
not escaped_brace(fmt, start) and
fmt.getText().charAt(start) = "{" and
exists(string pair |
pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1)
|
end = start + pair.length()
)
not escaping_brace(fmt, start) and
not escaped_brace(fmt, start) and
fmt.getText().charAt(start) = "{" and
exists(string pair |
pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1)
|
end = start + pair.length()
)
}
private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) {
inner_brace_pair(fmt, start, end)
or
not escaping_brace(fmt, start) and
not escaped_brace(fmt, start) and
exists(string prefix, string postfix, int innerstart, int innerend |
brace_pair(fmt, innerstart, innerend) and
prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and
innerstart = start + prefix.length() - 1 and
postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and
end = innerend + postfix.length() - 1
)
inner_brace_pair(fmt, start, end)
or
not escaping_brace(fmt, start) and
not escaped_brace(fmt, start) and
exists(string prefix, string postfix, int innerstart, int innerend |
brace_pair(fmt, innerstart, innerend) and
prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and
innerstart = start + prefix.length() - 1 and
postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and
end = innerend + postfix.length() - 1
)
}
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
exists(CallNode call | call = format_expr.getAFlowNode() |
call.getFunction().pointsTo(Value::named("format")) and
call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and
args = count(format_expr.getAnArg()) - 1
or
call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and
args = count(format_expr.getAnArg())
)
exists(CallNode call | call = format_expr.getAFlowNode() |
call.getFunction().pointsTo(Value::named("format")) and
call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and
args = count(format_expr.getAnArg()) - 1
or
call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and
args = count(format_expr.getAnArg())
)
}
/** A string constant that has the `format` method applied to it. */
class AdvancedFormatString extends PossibleAdvancedFormatString {
AdvancedFormatString() { advanced_format_call(_, this, _) }
AdvancedFormatString() { advanced_format_call(_, this, _) }
}
/** A string formatting operation that uses the `format` method. */
class AdvancedFormattingCall extends Call {
AdvancedFormattingCall() { advanced_format_call(this, _, _) }
AdvancedFormattingCall() { advanced_format_call(this, _, _) }
/** Count of the arguments actually provided */
int providedArgCount() { advanced_format_call(this, _, result) }
/** Count of the arguments actually provided */
int providedArgCount() { advanced_format_call(this, _, result) }
/** Gets a formatting string for this call. */
AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) }
/** Gets a formatting string for this call. */
AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) }
}

View File

@@ -18,11 +18,11 @@ int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _
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)
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() + "\""
"Too many arguments for string format. Format $@ requires only " + max_field + ", but " +
arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\""

View File

@@ -15,17 +15,17 @@ 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."
)
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
"Surplus named argument for string format. An argument named '" + name +
"' is provided, but it is not required by $@.", fmt, fmt_repr

View File

@@ -16,10 +16,10 @@ 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())
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() + "\""
"Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.",
fmt, "\"" + fmt.getText() + "\""

View File

@@ -15,15 +15,15 @@ import python
import AdvancedFormatting
from
AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field,
string provided
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.")
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() + "\""
"Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " +
arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\""

View File

@@ -19,39 +19,39 @@ import python
*/
predicate numpy_array_type(ClassValue na) {
exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" |
na.getASuperType() = np.attr("ndarray")
)
exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" |
na.getASuperType() = np.attr("ndarray")
)
}
predicate has_custom_getitem(Value v) {
v.getClass().lookup("__getitem__") instanceof PythonFunctionValue
or
numpy_array_type(v.getClass())
v.getClass().lookup("__getitem__") instanceof PythonFunctionValue
or
numpy_array_type(v.getClass())
}
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) {
is_unhashable(f, c, origin) and
exists(SubscriptNode sub | sub.getIndex() = f |
exists(Value custom_getitem |
sub.getObject().pointsTo(custom_getitem) and
not has_custom_getitem(custom_getitem)
)
is_unhashable(f, c, origin) and
exists(SubscriptNode sub | sub.getIndex() = f |
exists(Value custom_getitem |
sub.getObject().pointsTo(custom_getitem) and
not has_custom_getitem(custom_getitem)
)
)
}
predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origin) {
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")
)
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")
)
}
/**
@@ -68,18 +68,18 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi
* it.
*/
predicate typeerror_is_caught(ControlFlowNode f) {
exists(Try try |
try.getBody().contains(f.getNode()) and
try.getAHandler().getType().pointsTo(ClassValue::typeError())
)
exists(Try try |
try.getBody().contains(f.getNode()) and
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

@@ -15,13 +15,13 @@ 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 = "!="
)
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."
"Values compared using '" + op.getSymbol() +
"' when equivalence is not the same as identity. Use '" + alt + "' instead."

View File

@@ -4,60 +4,60 @@ import python
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
fcomp.operands(left, op, right) and
(op instanceof Is or op instanceof IsNot)
)
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
fcomp.operands(left, op, right) and
(op instanceof Is or op instanceof IsNot)
)
}
/** Holds if the class `c` overrides the default notion of equality or comparison. */
predicate overrides_eq_or_cmp(ClassValue c) {
major_version() = 2 and c.hasAttribute("__eq__")
or
c.declaresAttribute("__eq__") and not c = Value::named("object")
or
exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") |
sup.declaresAttribute("__eq__")
)
or
major_version() = 2 and c.hasAttribute("__cmp__")
major_version() = 2 and c.hasAttribute("__eq__")
or
c.declaresAttribute("__eq__") and not c = Value::named("object")
or
exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") |
sup.declaresAttribute("__eq__")
)
or
major_version() = 2 and c.hasAttribute("__cmp__")
}
/** Holds if the class `cls` is likely to only have a single instance throughout the program. */
predicate probablySingleton(ClassValue cls) {
strictcount(Value inst | inst.getClass() = cls) = 1
or
cls = Value::named("None").getClass()
strictcount(Value inst | inst.getClass() = cls) = 1
or
cls = Value::named("None").getClass()
}
/** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */
predicate invalid_to_use_is_portably(ClassValue c) {
overrides_eq_or_cmp(c) and
// Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__
not c = Value::named("type") and
not c = ClassValue::builtinFunction() and
not c = Value::named("bool") and
// OK to compare with 'is' if a singleton
not probablySingleton(c)
overrides_eq_or_cmp(c) and
// Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__
not c = Value::named("type") and
not c = ClassValue::builtinFunction() and
not c = Value::named("bool") and
// OK to compare with 'is' if a singleton
not probablySingleton(c)
}
/** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */
predicate simple_constant(ControlFlowNode f) {
exists(Value val | f.pointsTo(val) |
val = Value::named("True") or val = Value::named("False") or val = Value::named("None")
)
exists(Value val | f.pointsTo(val) |
val = Value::named("True") or val = Value::named("False") or val = Value::named("None")
)
}
private predicate cpython_interned_value(Expr e) {
exists(string text | text = e.(StrConst).getText() |
text.length() = 0
or
text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]")
)
exists(string text | text = e.(StrConst).getText() |
text.length() = 0
or
exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256)
or
exists(Tuple t | t = e and not exists(t.getAnElt()))
text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]")
)
or
exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256)
or
exists(Tuple t | t = e and not exists(t.getAnElt()))
}
/**
@@ -66,83 +66,83 @@ private predicate cpython_interned_value(Expr e) {
* follow CPython, but it varies, so this is a best guess.
*/
private predicate universally_interned_value(Expr e) {
e.(IntegerLiteral).getN().toInt() = 0
or
exists(Tuple t | t = e and not exists(t.getAnElt()))
or
e.(StrConst).getText() = ""
e.(IntegerLiteral).getN().toInt() = 0
or
exists(Tuple t | t = e and not exists(t.getAnElt()))
or
e.(StrConst).getText() = ""
}
/** Holds if the expression `e` points to an interned constant in CPython. */
predicate cpython_interned_constant(Expr e) {
exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const))
exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const))
}
/** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */
predicate universally_interned_constant(Expr e) {
exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const))
exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const))
}
private predicate comparison_both_types(Compare comp, Cmpop op, ClassValue cls1, ClassValue cls2) {
exists(ControlFlowNode op1, ControlFlowNode op2 |
comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1)
|
op1.inferredValue().getClass() = cls1 and
op2.inferredValue().getClass() = cls2
)
exists(ControlFlowNode op1, ControlFlowNode op2 |
comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1)
|
op1.inferredValue().getClass() = cls1 and
op2.inferredValue().getClass() = cls2
)
}
private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) {
not comparison_both_types(comp, _, _, _) and
exists(ControlFlowNode operand |
comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand)
|
operand.inferredValue().getClass() = cls
)
not comparison_both_types(comp, _, _, _) and
exists(ControlFlowNode operand |
comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand)
|
operand.inferredValue().getClass() = cls
)
}
/**
* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`.
* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`.
*/
predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) {
// OK to use 'is' when defining '__eq__'
not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" |
eq = comp.getScope().getScope*()
) and
(
comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls)
or
exists(ClassValue other | comparison_both_types(comp, op, cls, other) |
invalid_to_use_is_portably(cls) and
invalid_to_use_is_portably(other)
)
) and
// OK to use 'is' when comparing items from a known set of objects
not exists(Expr left, Expr right, Value val |
comp.compares(left, op, right) and
exists(ImmutableLiteral il | il.getLiteralValue() = val)
|
left.pointsTo(val) and right.pointsTo(val)
or
// Simple constant in module, probably some sort of sentinel
exists(AstNode origin |
not left.pointsTo(_) and
right.pointsTo(val, origin) and
origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule()
)
) and
// OK to use 'is' when comparing with a member of an enum
not exists(Expr left, Expr right, AstNode origin |
comp.compares(left, op, right) and
enum_member(origin)
|
left.pointsTo(_, origin) or right.pointsTo(_, origin)
// OK to use 'is' when defining '__eq__'
not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" |
eq = comp.getScope().getScope*()
) and
(
comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls)
or
exists(ClassValue other | comparison_both_types(comp, op, cls, other) |
invalid_to_use_is_portably(cls) and
invalid_to_use_is_portably(other)
)
) and
// OK to use 'is' when comparing items from a known set of objects
not exists(Expr left, Expr right, Value val |
comp.compares(left, op, right) and
exists(ImmutableLiteral il | il.getLiteralValue() = val)
|
left.pointsTo(val) and right.pointsTo(val)
or
// Simple constant in module, probably some sort of sentinel
exists(AstNode origin |
not left.pointsTo(_) and
right.pointsTo(val, origin) and
origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule()
)
) and
// OK to use 'is' when comparing with a member of an enum
not exists(Expr left, Expr right, AstNode origin |
comp.compares(left, op, right) and
enum_member(origin)
|
left.pointsTo(_, origin) or right.pointsTo(_, origin)
)
}
private predicate enum_member(AstNode obj) {
exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" |
cls.getScope() = asgn.getScope() and
asgn.getValue() = obj
)
exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" |
cls.getScope() = asgn.getScope() and
asgn.getValue() = obj
)
}

View File

@@ -16,12 +16,12 @@ 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)
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

@@ -15,11 +15,11 @@ 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)
)
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."
"The result of this comparison with '" + op.getSymbol() +
"' may differ between implementations of Python."

View File

@@ -4,53 +4,54 @@ import python
/** A comparison where the left and right hand sides appear to be identical. */
class RedundantComparison extends Compare {
RedundantComparison() {
exists(Expr left, Expr right |
this.compares(left, _, right) and
same_variable(left, right)
)
}
RedundantComparison() {
exists(Expr left, Expr right |
this.compares(left, _, right) and
same_variable(left, right)
)
}
/** Holds if this comparison could be redundant due to a missing `self.`, for example
* ```python
* foo == foo
* ```
* instead of
* ```python
* self.foo == foo
* ```
*/
predicate maybeMissingSelf() {
exists(Name left |
this.compares(left, _, _) and
not this.isConstant() and
exists(Class cls | left.getScope().getScope() = cls |
exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls)
)
)
}
/**
* Holds if this comparison could be redundant due to a missing `self.`, for example
* ```python
* foo == foo
* ```
* instead of
* ```python
* self.foo == foo
* ```
*/
predicate maybeMissingSelf() {
exists(Name left |
this.compares(left, _, _) and
not this.isConstant() and
exists(Class cls | left.getScope().getScope() = cls |
exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls)
)
)
}
}
private predicate same_variable(Expr left, Expr right) {
same_name(left, right)
or
same_attribute(left, right)
same_name(left, right)
or
same_attribute(left, right)
}
private predicate name_in_comparison(Compare comp, Name n, Variable v) {
comp.contains(n) and v = n.getVariable()
comp.contains(n) and v = n.getVariable()
}
private predicate same_name(Name n1, Name n2) {
n1 != n2 and
exists(Compare comp, Variable v |
name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v)
)
n1 != n2 and
exists(Compare comp, Variable v |
name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v)
)
}
private predicate same_attribute(Attribute a1, Attribute a2) {
a1 != a2 and
exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and
a1.getName() = a2.getName() and
same_name(a1.getObject(), a2.getObject())
a1 != a2 and
exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and
a1.getName() = a2.getName() and
same_name(a1.getObject(), a2.getObject())
}

View File

@@ -15,7 +15,7 @@ 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))
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

@@ -14,29 +14,29 @@ import python
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
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")
)
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
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")
)
}
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."
"This regular expression includes duplicate character '" + char + "' in a set of characters."

View File

@@ -14,13 +14,13 @@ import python
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)
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
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() + "."
"This regular expression includes an unmatchable caret at offset " + offset.toString() + "."

View File

@@ -14,13 +14,13 @@ import python
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)
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
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() + "."
"This regular expression includes an unmatchable dollar at offset " + offset.toString() + "."

View File

@@ -15,23 +15,23 @@ 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
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_() and
// Ignore instances where integer division leaves no remainder
not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and
not bin.getNode().getEnclosingModule().hasFromFuture("division") and
// Filter out results wrapped in `int(...)`
not exists(CallNode c |
c = ClassValue::int_().getACall() and
c.getAnArg() = bin
)
// Only relevant for Python 2, as all later versions implement true division
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_() and
// Ignore instances where integer division leaves no remainder
not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and
not bin.getNode().getEnclosingModule().hasFromFuture("division") and
// Filter out results wrapped in `int(...)`
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

@@ -15,20 +15,20 @@
import python
predicate string_const(Expr s) {
s instanceof StrConst
or
string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight())
s instanceof StrConst
or
string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight())
}
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

@@ -14,48 +14,48 @@ 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) = 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())
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) = 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
// f is not necessarily a drop-in replacement for the lambda if there are default argument values
not exists(l.getArgs().getADefault())
/* 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
(
/* 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())
)
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 |
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."
"This 'lambda' is just a simple wrapper around a callable object. Use that object directly."

View File

@@ -14,8 +14,8 @@ import python
from CallNode call, Context context, ControlFlowNode func
where
context.getAVersion().includes(2, _) and
call.getFunction() = func and
func.pointsTo(context, Value::named("input"), _) and
not func.pointsTo(context, Value::named("raw_input"), _)
context.getAVersion().includes(2, _) and
call.getFunction() = func and
func.pointsTo(context, Value::named("input"), _) and
not func.pointsTo(context, Value::named("raw_input"), _)
select call, "The unsafe built-in function 'input' is used in Python 2."

View File

@@ -18,10 +18,10 @@ 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
)
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()
func.descriptiveString()

View File

@@ -16,32 +16,32 @@ import python
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.getOp() instanceof Mod and
exists(Value fmt, Context ctx |
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) |
result = strictcount(seq.getAnElt()) and
not seq.getAnElt() instanceof Starred
)
or
exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1)
/* Guess length of sequence */
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)
}
from
BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin,
string provided
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.")
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()
"Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " +
slen.toString() + provided, origin, "arguments", fmt, fmt.getText()

View File

@@ -16,15 +16,16 @@ import CallArgs
from Call call, FunctionValue func, string too, string should, int limit
where
(
(
too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than "
or
too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than "
) and
not isAbstract(func) and
not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(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()
) and
not isAbstract(func) and
not exists(FunctionValue overridden |
func.overrides(overridden) and correct_args_if_called_as_method(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,9 +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

@@ -13,21 +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(Return return | return.getScope() = func and not exists(return.getValue()))
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."
"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
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

@@ -14,10 +14,10 @@ import python
from Return r, Expr rv
where
exists(Function init | init.isInitMethod() and r.getScope() = init) and
r.getValue() = rv and
not rv.pointsTo(Value::none_()) and
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
// to avoid double reporting, don't trigger if returning result from other __init__ function
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
exists(Function init | init.isInitMethod() and r.getScope() = init) and
r.getValue() = rv and
not rv.pointsTo(Value::none_()) and
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
// to avoid double reporting, don't trigger if returning result from other __init__ function
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
select r, "Explicit return in __init__ method."

View File

@@ -14,142 +14,142 @@
import python
private predicate attribute_method(string name) {
name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
}
private predicate indexing_method(string name) {
name = "__getitem__" or name = "__setitem__" or name = "__delitem__"
name = "__getitem__" or name = "__setitem__" or name = "__delitem__"
}
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 = "__cmp__" and major_version() = 2
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 = "__int__"
or
name = "__float__"
or
name = "__long__"
or
name = "__trunc__"
or
name = "__complex__"
name = "__nonzero__" and major_version() = 2
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
(
name = "__copy__" or
name = "__deepcopy__" or
name = "__call__" or
indexing_method(name) or
attribute_method(name)
)
or
preferred_raise(name, ex)
or
preferred_raise(name, ex.getASuperType())
ex.getAnImproperSuperType() = theTypeErrorType() and
(
name = "__copy__" or
name = "__deepcopy__" or
name = "__call__" or
indexing_method(name) or
attribute_method(name)
)
or
preferred_raise(name, ex)
or
preferred_raise(name, ex.getASuperType())
}
predicate preferred_raise(string name, ClassObject ex) {
attribute_method(name) and ex = theAttributeErrorType()
or
indexing_method(name) and ex = Object::builtin("LookupError")
or
ordering_method(name) and ex = theTypeErrorType()
or
arithmetic_method(name) and ex = Object::builtin("ArithmeticError")
or
name = "__bool__" and ex = theTypeErrorType()
attribute_method(name) and ex = theAttributeErrorType()
or
indexing_method(name) and ex = Object::builtin("LookupError")
or
ordering_method(name) and ex = theTypeErrorType()
or
arithmetic_method(name) and ex = Object::builtin("ArithmeticError")
or
name = "__bool__" and ex = theTypeErrorType()
}
predicate no_need_to_raise(string name, string message) {
name = "__hash__" and message = "use __hash__ = None instead"
or
cast_method(name) and message = "there is no need to implement the method at all."
name = "__hash__" and message = "use __hash__ = None instead"
or
cast_method(name) and message = "there is no need to implement the method at all."
}
predicate is_abstract(FunctionObject func) {
func.getFunction().getADecorator().(Name).getId().matches("%abstract%")
func.getFunction().getADecorator().(Name).getId().matches("%abstract%")
}
predicate always_raises(FunctionObject f, ClassObject ex) {
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 */
not ex = theStopIterationType()
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 */
not ex = theStopIterationType()
}
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"
)
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,18 +14,18 @@ import Expressions.CallArgs
from Call call, FunctionValue func, FunctionValue overridden, string problem
where
func.overrides(overridden) and
(
wrong_args(call, func, _, problem) and
correct_args_if_called_as_method(call, overridden)
or
exists(string name |
illegally_named_parameter(call, func, name) and
problem = "an argument named '" + name + "'" and
overridden.getScope().getAnArg().(Name).getId() = name
)
func.overrides(overridden) and
(
wrong_args(call, func, _, problem) and
correct_args_if_called_as_method(call, overridden)
or
exists(string name |
illegally_named_parameter(call, func, name) and
problem = "an argument named '" + name + "'" and
overridden.getScope().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()
"Overriding method signature does not match $@, where it is passed " + problem +
". Overridden method $@ is correctly specified.", call, "here", overridden,
overridden.descriptiveString()

View File

@@ -15,23 +15,23 @@ import Expressions.CallArgs
from Call call, FunctionValue func, FunctionValue overriding, string problem
where
not func.getName() = "__init__" and
overriding.overrides(func) and
call = overriding.getAMethodCall().getNode() and
correct_args_if_called_as_method(call, overriding) and
(
arg_count(call) + 1 < func.minParameters() and problem = "too few arguments"
or
arg_count(call) >= func.maxParameters() and problem = "too many arguments"
or
exists(string name |
call.getAKeyword().getArg() = name and
overriding.getScope().getAnArg().(Name).getId() = name and
not func.getScope().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(call, overriding) and
(
arg_count(call) + 1 < func.minParameters() and problem = "too few arguments"
or
arg_count(call) >= func.maxParameters() and problem = "too many arguments"
or
exists(string name |
call.getAKeyword().getArg() = name and
overriding.getScope().getAnArg().(Name).getId() = name and
not func.getScope().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()
"Overridden method signature does not match $@, where it is passed " + problem +
". Overriding method $@ matches the call.", call, "call", overriding,
overriding.descriptiveString()

View File

@@ -14,6 +14,6 @@ import python
from Function f
where
f.isInitMethod() and
(exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f))
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

@@ -14,10 +14,10 @@ import python
from ClassValue iterable, FunctionValue iter, ClassValue iterator
where
iter = iterable.lookup("__iter__") and
iterator = iter.getAnInferredReturnType() and
not iterator.isIterator()
iter = iterable.lookup("__iter__") and
iterator = iter.getAnInferredReturnType() and
not iterator.isIterator()
select iterator,
"Class " + iterator.getName() +
" is returned as an iterator (by $@) but does not fully implement the iterator interface.",
iter, iter.getName()
"Class " + iterator.getName() +
" is returned as an iterator (by $@) but does not fully implement the iterator interface.",
iter, iter.getName()

View File

@@ -17,14 +17,14 @@ Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValu
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 ClassValue 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()
iter, iter.getName()

View File

@@ -15,85 +15,85 @@ import python
import semmle.python.security.Paths
predicate safe_method(string name) {
name = "count" or
name = "index" or
name = "copy" or
name = "get" or
name = "has_key" or
name = "items" or
name = "keys" or
name = "values" or
name = "iteritems" or
name = "iterkeys" or
name = "itervalues" or
name = "__contains__" or
name = "__getitem__" or
name = "__getattribute__"
name = "count" or
name = "index" or
name = "copy" or
name = "get" or
name = "has_key" or
name = "items" or
name = "keys" or
name = "values" or
name = "iteritems" or
name = "iterkeys" or
name = "itervalues" or
name = "__contains__" or
name = "__getitem__" or
name = "__getattribute__"
}
/** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */
private boolean mutableDefaultValue(Parameter p) {
exists(Dict d | p.getDefault() = d |
exists(d.getAKey()) and result = true
or
not exists(d.getAKey()) and result = false
)
exists(Dict d | p.getDefault() = d |
exists(d.getAKey()) and result = true
or
exists(List l | p.getDefault() = l |
exists(l.getAnElt()) and result = true
or
not exists(l.getAnElt()) and result = false
)
not exists(d.getAKey()) and result = false
)
or
exists(List l | p.getDefault() = l |
exists(l.getAnElt()) and result = true
or
not exists(l.getAnElt()) and result = false
)
}
class NonEmptyMutableValue extends TaintKind {
NonEmptyMutableValue() { this = "non-empty mutable value" }
NonEmptyMutableValue() { this = "non-empty mutable value" }
}
class EmptyMutableValue extends TaintKind {
EmptyMutableValue() { this = "empty mutable value" }
EmptyMutableValue() { this = "empty mutable value" }
override boolean booleanValue() { result = false }
override boolean booleanValue() { result = false }
}
class MutableDefaultValue extends TaintSource {
boolean nonEmpty;
boolean nonEmpty;
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) }
MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) }
override string toString() { result = "mutable default value" }
override string toString() { result = "mutable default value" }
override predicate isSourceOf(TaintKind kind) {
nonEmpty = false and kind instanceof EmptyMutableValue
or
nonEmpty = true and kind instanceof NonEmptyMutableValue
}
override predicate isSourceOf(TaintKind kind) {
nonEmpty = false and kind instanceof EmptyMutableValue
or
nonEmpty = true and kind instanceof NonEmptyMutableValue
}
}
private ClassValue mutable_class() {
result = Value::named("list") or
result = Value::named("dict")
result = Value::named("list") or
result = Value::named("dict")
}
class Mutation extends TaintSink {
Mutation() {
exists(AugAssign a | a.getTarget().getAFlowNode() = this)
or
exists(Call c, Attribute a | c.getFunc() = a |
a.getObject().getAFlowNode() = this and
not safe_method(a.getName()) and
this.(ControlFlowNode).pointsTo().getClass() = mutable_class()
)
}
Mutation() {
exists(AugAssign a | a.getTarget().getAFlowNode() = this)
or
exists(Call c, Attribute a | c.getFunc() = a |
a.getObject().getAFlowNode() = this and
not safe_method(a.getName()) and
this.(ControlFlowNode).pointsTo().getClass() = mutable_class()
)
}
override predicate sinks(TaintKind kind) {
kind instanceof EmptyMutableValue
or
kind instanceof NonEmptyMutableValue
}
override predicate sinks(TaintKind kind) {
kind instanceof EmptyMutableValue
or
kind instanceof NonEmptyMutableValue
}
}
from TaintedPathSource src, TaintedPathSink sink
where src.flowsTo(sink)
select sink.getSink(), src, sink, "$@ flows to here and is mutated.", src.getSource(),
"Default value"
"Default value"

View File

@@ -15,36 +15,36 @@
import python
predicate first_arg_cls(Function f) {
exists(string argname | argname = f.getArgName(0) |
argname = "cls"
or
/* Not PEP8, but relatively common */
argname = "mcls"
)
exists(string argname | argname = f.getArgName(0) |
argname = "cls"
or
/* Not PEP8, but relatively common */
argname = "mcls"
)
}
predicate is_type_method(Function f) {
exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type())
exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type())
}
predicate classmethod_decorators_only(Function f) {
forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod")
forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod")
}
from Function f, string message
where
(f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and
not first_arg_cls(f) and
classmethod_decorators_only(f) and
not f.getName() = "__new__" and
(
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."
else
message =
"Class methods or methods of a type deriving from type should have 'cls' as their first parameter."
)
(f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and
not first_arg_cls(f) and
classmethod_decorators_only(f) and
not f.getName() = "__new__" and
(
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."
else
message =
"Class methods or methods of a type deriving from type should have 'cls' as their first parameter."
)
select f, message

View File

@@ -17,42 +17,42 @@ import python
import semmle.python.libraries.Zope
predicate is_type_method(FunctionValue fv) {
exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type())
exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type())
}
predicate used_in_defining_scope(FunctionValue fv) {
exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv))
exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv))
}
from Function f, FunctionValue fv, string message
where
exists(ClassValue cls, string name |
cls.declaredAttribute(name) = fv and
cls.isNewStyle() and
not name = "__new__" and
not name = "__metaclass__" and
not name = "__init_subclass__" and
not name = "__class_getitem__" and
/* declared in scope */
f.getScope() = cls.getScope()
) and
not f.getArgName(0) = "self" and
not is_type_method(fv) and
fv.getScope() = f and
not f.getName() = "lambda" and
not used_in_defining_scope(fv) and
exists(ClassValue cls, string name |
cls.declaredAttribute(name) = fv and
cls.isNewStyle() and
not name = "__new__" and
not name = "__metaclass__" and
not name = "__init_subclass__" and
not name = "__class_getitem__" and
/* declared in scope */
f.getScope() = cls.getScope()
) and
not f.getArgName(0) = "self" and
not is_type_method(fv) and
fv.getScope() = f and
not f.getName() = "lambda" and
not used_in_defining_scope(fv) and
(
(
(
if exists(f.getArgName(0))
then
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')."
) and
not f.hasVarArg()
if exists(f.getArgName(0))
then
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')."
) and
not fv instanceof ZopeInterfaceMethodValue
not f.hasVarArg()
) and
not fv instanceof ZopeInterfaceMethodValue
select f, message

View File

@@ -17,8 +17,8 @@ import python
from FunctionValue method
where
exists(ClassValue c |
c.declaredAttribute("__del__") = method and
method.getScope().getMetrics().getCyclomaticComplexity() > 3
)
exists(ClassValue c |
c.declaredAttribute("__del__") = method and
method.getScope().getMetrics().getCyclomaticComplexity() > 3
)
select method, "Overly complex '__del__' method."

View File

@@ -13,18 +13,18 @@
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)
|
size = val.length()
)
exists(Return return, TupleValue val |
return.getScope() = func and
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
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
"tuple of size " + s2

View File

@@ -18,66 +18,66 @@ import python
import semmle.python.objects.Callables
predicate meaningful_return_value(Expr val) {
val instanceof Name
or
val instanceof BooleanLiteral
or
exists(FunctionValue callee |
val = callee.getACall().getNode() and returns_meaningful_value(callee)
)
or
not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name
val instanceof Name
or
val instanceof BooleanLiteral
or
exists(FunctionValue callee |
val = callee.getACall().getNode() and returns_meaningful_value(callee)
)
or
not exists(FunctionValue 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(FunctionValue f) {
not exists(f.getScope().getFallthroughNode()) and
(
exists(Return ret, Expr val | ret.getScope() = f.getScope() 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.isBuiltin() and
f.getAnInferredReturnType() != ClassValue::nonetype() and
not f.getName() = "__import__"
not exists(f.getScope().getFallthroughNode()) and
(
exists(Return ret, Expr val | ret.getScope() = f.getScope() 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.isBuiltin() and
f.getAnInferredReturnType() != ClassValue::nonetype() and
not f.getName() = "__import__"
)
}
/* If a call is wrapped tightly in a try-except then we assume it is being executed for the exception. */
predicate wrapped_in_try_except(ExprStmt call) {
exists(Try t |
exists(t.getAHandler()) and
strictcount(Call c | t.getBody().contains(c)) = 1 and
call = t.getAStmt()
)
exists(Try t |
exists(t.getAHandler()) and
strictcount(Call c | t.getBody().contains(c)) = 1 and
call = t.getAStmt()
)
}
from ExprStmt call, FunctionValue 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
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()
"Call discards return value of function $@. The result is used in " + percentage_used.toString() +
"% of calls.", callee, callee.getName()

View File

@@ -16,20 +16,20 @@ import Expressions.CallArgs
from FunctionValue base, PythonFunctionValue derived
where
not exists(base.getACall()) and
not exists(FunctionValue a_derived |
a_derived.overrides(base) and
exists(a_derived.getACall())
) and
not derived.getScope().isSpecialMethod() and
derived.getName() != "__init__" and
derived.isNormalMethod() and
not derived.getScope().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()
)
not exists(base.getACall()) and
not exists(FunctionValue a_derived |
a_derived.overrides(base) and
exists(a_derived.getACall())
) and
not derived.getScope().isSpecialMethod() and
derived.getName() != "__init__" and
derived.isNormalMethod() and
not derived.getScope().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"
base, "overridden method"

View File

@@ -13,200 +13,200 @@
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__" }
int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
cls.declaredAttribute(name) = f and
(
is_unary_op(name) and result = 1
or
is_binary_op(name) and result = 2
or
is_ternary_op(name) and result = 3
or
is_quad_op(name) and result = 4
)
cls.declaredAttribute(name) = f and
(
is_unary_op(name) and result = 1
or
is_binary_op(name) and result = 2
or
is_ternary_op(name) and result = 3
or
is_quad_op(name) and result = 4
)
}
predicate incorrect_special_method_defn(
PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner
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()
)
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
)
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
)
}
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
)
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
)
}
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) {
exists(int i | i = f.minParameters() |
i = 0 and result = "no parameters"
or
i = 1 and result = "1 parameter"
or
i > 1 and result = i.toString() + " parameters"
)
exists(int i | i = f.minParameters() |
i = 0 and result = "no parameters"
or
i = 1 and result = "1 parameter"
or
i > 1 and result = i.toString() + " parameters"
)
}
from
PythonFunctionValue f, string message, string sizes, boolean show_counts, string name,
ClassValue owner
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)
)
(
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,26 +13,26 @@ 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
)
or
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. */
not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1)
)
exists(Expr outer | outer != c and outer.containsInScope(c) |
outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript
)
or
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. */
not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1)
)
}
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())
/* 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

@@ -3,84 +3,84 @@ import python
predicate is_import_time(Stmt s) { not s.getScope+() instanceof Function }
ModuleValue module_imported_by(ModuleValue m) {
exists(Stmt imp |
result = stmt_imports(imp) and
imp.getEnclosingModule() = m.getScope() and
// Import must reach exit to be part of a cycle
imp.getAnEntryNode().getBasicBlock().reachesExit()
)
exists(Stmt imp |
result = stmt_imports(imp) and
imp.getEnclosingModule() = m.getScope() and
// Import must reach exit to be part of a cycle
imp.getAnEntryNode().getBasicBlock().reachesExit()
)
}
/** Is there a circular import of 'm1' beginning with 'm2'? */
predicate circular_import(ModuleValue m1, ModuleValue m2) {
m1 != m2 and
m2 = module_imported_by(m1) and
m1 = module_imported_by+(m2)
m1 != m2 and
m2 = module_imported_by(m1) and
m1 = module_imported_by+(m2)
}
ModuleValue stmt_imports(ImportingStmt s) {
exists(string name | result.importedAs(name) and not name = "__main__" |
name = s.getAnImportedModuleName() and
s.getASubExpression().pointsTo(result) and
not result.isPackage()
)
exists(string name | result.importedAs(name) and not name = "__main__" |
name = s.getAnImportedModuleName() and
s.getASubExpression().pointsTo(result) and
not result.isPackage()
)
}
predicate import_time_imported_module(ModuleValue m1, ModuleValue m2, Stmt imp) {
imp.getEnclosingModule() = m1.getScope() and
is_import_time(imp) and
m2 = stmt_imports(imp)
imp.getEnclosingModule() = m1.getScope() and
is_import_time(imp) and
m2 = stmt_imports(imp)
}
/** Is there a cyclic import of 'm1' beginning with an import 'm2' at 'imp' where all the imports are top-level? */
predicate import_time_circular_import(ModuleValue m1, ModuleValue m2, Stmt imp) {
m1 != m2 and
import_time_imported_module(m1, m2, imp) and
import_time_transitive_import(m2, _, m1)
m1 != m2 and
import_time_imported_module(m1, m2, imp) and
import_time_transitive_import(m2, _, m1)
}
predicate import_time_transitive_import(ModuleValue base, Stmt imp, ModuleValue last) {
last != base and
(
import_time_imported_module(base, last, imp)
or
exists(ModuleValue mid |
import_time_transitive_import(base, imp, mid) and
import_time_imported_module(mid, last, _)
)
) and
// Import must reach exit to be part of a cycle
imp.getAnEntryNode().getBasicBlock().reachesExit()
last != base and
(
import_time_imported_module(base, last, imp)
or
exists(ModuleValue mid |
import_time_transitive_import(base, imp, mid) and
import_time_imported_module(mid, last, _)
)
) and
// Import must reach exit to be part of a cycle
imp.getAnEntryNode().getBasicBlock().reachesExit()
}
/**
* Returns import-time usages of module 'm' in module 'enclosing'
*/
predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use, string attr) {
exists(Expr mod |
use.getEnclosingModule() = enclosing.getScope() and
not use.getScope+() instanceof Function and
mod.pointsTo(m) and
not is_annotation_with_from_future_import_annotations(use)
|
// either 'M.foo'
use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr
or
// or 'from M import foo'
use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr
)
exists(Expr mod |
use.getEnclosingModule() = enclosing.getScope() and
not use.getScope+() instanceof Function and
mod.pointsTo(m) and
not is_annotation_with_from_future_import_annotations(use)
|
// either 'M.foo'
use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr
or
// or 'from M import foo'
use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr
)
}
/**
* Holds if `use` appears inside an annotation.
*/
predicate is_used_in_annotation(Expr use) {
exists(FunctionExpr f |
f.getReturns().getASubExpression*() = use or
f.getArgs().getAnAnnotation().getASubExpression*() = use
)
or
exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use)
exists(FunctionExpr f |
f.getReturns().getASubExpression*() = use or
f.getArgs().getAnAnnotation().getASubExpression*() = use
)
or
exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use)
}
/**
@@ -89,10 +89,10 @@ predicate is_used_in_annotation(Expr use) {
* See https://www.python.org/dev/peps/pep-0563/
*/
predicate is_annotation_with_from_future_import_annotations(Expr use) {
exists(ImportMember i | i.getScope() = use.getEnclosingModule() |
i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations"
) and
is_used_in_annotation(use)
exists(ImportMember i | i.getScope() = use.getEnclosingModule() |
i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations"
) and
is_used_in_annotation(use)
}
/**
@@ -101,18 +101,18 @@ predicate is_annotation_with_from_future_import_annotations(Expr use) {
* occur after the import 'other' in 'first'.
*/
predicate failing_import_due_to_cycle(
ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr
ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr
) {
import_time_imported_module(other, first, _) and
import_time_transitive_import(first, imp, other) and
import_time_module_use(first, other, use, attr) and
exists(ImportTimeScope n, SsaVariable v |
defn = v.getDefinition() and
n = first.getScope() and
v.getVariable().getScope() = n and
v.getId() = attr
|
not defn.strictlyDominates(imp.getAnEntryNode())
) and
not exists(If i | i.isNameEqMain() and i.contains(use))
import_time_imported_module(other, first, _) and
import_time_transitive_import(first, imp, other) and
import_time_module_use(first, other, use, attr) and
exists(ImportTimeScope n, SsaVariable v |
defn = v.getDefinition() and
n = first.getScope() and
v.getVariable().getScope() = n and
v.getId() = attr
|
not defn.strictlyDominates(imp.getAnEntryNode())
) and
not exists(If i | i.isNameEqMain() and i.contains(use))
}

View File

@@ -16,11 +16,11 @@ import Cyclic
from ModuleValue m1, ModuleValue m2, Stmt imp
where
imp.getEnclosingModule() = m1.getScope() and
stmt_imports(imp) = m2 and
circular_import(m1, m2) and
m1 != m2 and
// this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport
not failing_import_due_to_cycle(m2, m1, _, _, _, _) and
not exists(If i | i.isNameEqMain() and i.contains(imp))
imp.getEnclosingModule() = m1.getScope() and
stmt_imports(imp) = m2 and
circular_import(m1, m2) and
m1 != m2 and
// this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport
not failing_import_due_to_cycle(m2, m1, _, _, _, _) and
not exists(If i | i.isNameEqMain() and i.contains(imp))
select imp, "Import of module $@ begins an import cycle.", m2, m2.getName()

View File

@@ -17,69 +17,69 @@ import python
* and module `instead` should be used instead (or `instead = "no replacement"`)
*/
predicate deprecated_module(string name, string instead, int major, int minor) {
name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5
or
name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5
or
name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5
or
name = "pre" and instead = "re" and major = 1 and minor = 5
or
name = "whrandom" and instead = "random" and major = 2 and minor = 1
or
name = "rfc822" and instead = "email" and major = 2 and minor = 3
or
name = "mimetools" and instead = "email" and major = 2 and minor = 3
or
name = "MimeWriter" and instead = "email" and major = 2 and minor = 3
or
name = "mimify" and instead = "email" and major = 2 and minor = 3
or
name = "rotor" and instead = "no replacement" and major = 2 and minor = 4
or
name = "statcache" and instead = "no replacement" and major = 2 and minor = 2
or
name = "mpz" and instead = "a third party" and major = 2 and minor = 2
or
name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3
or
name = "multifile" and instead = "email" and major = 2 and minor = 5
or
name = "sets" and instead = "builtins" and major = 2 and minor = 6
or
name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3
or
name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4
or
name = "macfs" and instead = "no replacement" and major = 2 and minor = 3
or
name = "md5" and instead = "hashlib" and major = 2 and minor = 5
or
name = "sha" and instead = "hashlib" and major = 2 and minor = 5
name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5
or
name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5
or
name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5
or
name = "pre" and instead = "re" and major = 1 and minor = 5
or
name = "whrandom" and instead = "random" and major = 2 and minor = 1
or
name = "rfc822" and instead = "email" and major = 2 and minor = 3
or
name = "mimetools" and instead = "email" and major = 2 and minor = 3
or
name = "MimeWriter" and instead = "email" and major = 2 and minor = 3
or
name = "mimify" and instead = "email" and major = 2 and minor = 3
or
name = "rotor" and instead = "no replacement" and major = 2 and minor = 4
or
name = "statcache" and instead = "no replacement" and major = 2 and minor = 2
or
name = "mpz" and instead = "a third party" and major = 2 and minor = 2
or
name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3
or
name = "multifile" and instead = "email" and major = 2 and minor = 5
or
name = "sets" and instead = "builtins" and major = 2 and minor = 6
or
name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3
or
name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4
or
name = "macfs" and instead = "no replacement" and major = 2 and minor = 3
or
name = "md5" and instead = "hashlib" and major = 2 and minor = 5
or
name = "sha" and instead = "hashlib" and major = 2 and minor = 5
}
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() + "." +
minor.toString() + "."
)
exists(int major, int minor | deprecated_module(mod, _, major, minor) |
result =
"The " + mod + " module was deprecated in version " + major.toString() + "." +
minor.toString() + "."
)
}
string replacement_message(string mod) {
exists(string instead | deprecated_module(mod, instead, _, _) |
result = " Use " + instead + " module instead." and not instead = "no replacement"
or
result = "" and instead = "no replacement"
)
exists(string instead | deprecated_module(mod, instead, _, _) |
result = " Use " + instead + " module instead." and not instead = "no replacement"
or
result = "" and instead = "no replacement"
)
}
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() |
except.getType().pointsTo(ClassValue::importError()) and
except.containsInScope(imp)
)
name = imp.getName() and
deprecated_module(name, instead, _, _) and
not exists(Try try, ExceptStmt except | except = try.getAHandler() |
except.getType().pointsTo(ClassValue::importError()) and
except.containsInScope(imp)
)
select imp, deprecation_message(name) + replacement_message(name)

View File

@@ -16,19 +16,19 @@ import semmle.python.filters.Tests
from ImportMember im, ModuleValue m, AttrNode store_attr, string name
where
m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and
im.getName() = name and
/* Modification must be in a function, so it can occur during lifetime of the import value */
store_attr.getScope() instanceof Function and
/* variable resulting from import must have a long lifetime */
not im.getScope() instanceof Function and
store_attr.isStore() and
store_attr.getObject(name).pointsTo(m) and
/* Import not in same module as modification. */
not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and
/* Modification is not in a test */
not store_attr.getScope().getScope*() instanceof TestScope
m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and
im.getName() = name and
/* Modification must be in a function, so it can occur during lifetime of the import value */
store_attr.getScope() instanceof Function and
/* variable resulting from import must have a long lifetime */
not im.getScope() instanceof Function and
store_attr.isStore() and
store_attr.getObject(name).pointsTo(m) and
/* Import not in same module as modification. */
not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and
/* Modification is not in a test */
not store_attr.getScope().getScope*() instanceof TestScope
select im,
"Importing the value of '" + name +
"' from $@ means that any change made to $@ will be not be observed locally.", m,
"module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName()
"Importing the value of '" + name +
"' from $@ means that any change made to $@ will be not be observed locally.", m,
"module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName()

View File

@@ -13,11 +13,11 @@
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

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