mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Python: Port a few queries to new API.
This commit is contained in:
@@ -13,19 +13,19 @@
|
||||
|
||||
import python
|
||||
|
||||
FunctionObject defines_equality(ClassObject c, string name) {
|
||||
CallableValue defines_equality(ClassValue c, string name) {
|
||||
(name = "__eq__" or major_version() = 2 and name = "__cmp__")
|
||||
and
|
||||
result = c.declaredAttribute(name)
|
||||
}
|
||||
|
||||
FunctionObject implemented_method(ClassObject c, string name) {
|
||||
CallableValue implemented_method(ClassValue c, string name) {
|
||||
result = defines_equality(c, name)
|
||||
or
|
||||
result = c.declaredAttribute("__hash__") and name = "__hash__"
|
||||
}
|
||||
|
||||
string unimplemented_method(ClassObject c) {
|
||||
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__")
|
||||
or
|
||||
@@ -33,14 +33,21 @@ string unimplemented_method(ClassObject c) {
|
||||
not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2
|
||||
}
|
||||
|
||||
predicate violates_hash_contract(ClassObject c, string present, string missing, Object method) {
|
||||
not c.unhashable() and
|
||||
missing = unimplemented_method(c) and
|
||||
method = implemented_method(c, present) and
|
||||
not c.unknowableAttributes()
|
||||
/** Holds if this class is unhashable */
|
||||
predicate unhashable(ClassValue cls) {
|
||||
cls.lookup("__hash__") = Value::named("None")
|
||||
or
|
||||
cls.lookup("__hash__").(CallableValue).neverReturns()
|
||||
}
|
||||
|
||||
from ClassObject c, string present, string missing, FunctionObject method
|
||||
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(_)
|
||||
}
|
||||
|
||||
from ClassValue c, string present, string missing, CallableValue method
|
||||
where violates_hash_contract(c, present, missing, method) and
|
||||
exists(c.getPyClass()) // Suppress results that aren't from source
|
||||
exists(c.getScope()) // Suppress results that aren't from source
|
||||
select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, c.getName()
|
||||
|
||||
@@ -25,26 +25,26 @@ predicate total_ordering(Class cls) {
|
||||
n.getId() = "total_ordering")
|
||||
}
|
||||
|
||||
FunctionObject implemented_method(ClassObject c, string name) {
|
||||
CallableValue implemented_method(ClassValue c, string name) {
|
||||
result = c.declaredAttribute(name) and name = equals_or_ne()
|
||||
}
|
||||
|
||||
string unimplemented_method(ClassObject c) {
|
||||
string unimplemented_method(ClassValue c) {
|
||||
not c.declaresAttribute(result) and result = equals_or_ne()
|
||||
}
|
||||
|
||||
predicate violates_equality_contract(ClassObject c, string present, string missing, FunctionObject method) {
|
||||
predicate violates_equality_contract(ClassValue c, string present, string missing, CallableValue method) {
|
||||
missing = unimplemented_method(c) and
|
||||
method = implemented_method(c, present) and
|
||||
not c.unknowableAttributes() and
|
||||
not total_ordering(c.getPyClass()) 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.getFunction() instanceof DelegatingEqualityMethod and
|
||||
not c.lookupAttribute(missing).(FunctionObject).getFunction() instanceof DelegatingEqualityMethod
|
||||
not method.getScope() instanceof DelegatingEqualityMethod and
|
||||
not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod
|
||||
}
|
||||
|
||||
from ClassObject c, string present, string missing, FunctionObject method
|
||||
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()
|
||||
|
||||
@@ -27,25 +27,25 @@ string ordering_name(int n) {
|
||||
result = "__ge__" and n = 4
|
||||
}
|
||||
|
||||
predicate overrides_ordering_method(ClassObject c, string name) {
|
||||
predicate overrides_ordering_method(ClassValue c, string name) {
|
||||
name = ordering_name(_) and
|
||||
(
|
||||
c.declaresAttribute(name)
|
||||
or
|
||||
exists(ClassObject sup |
|
||||
sup = c.getASuperType() and not sup = theObjectType() |
|
||||
exists(ClassValue sup |
|
||||
sup = c.getASuperType() and not sup = Value::named("object") |
|
||||
sup.declaresAttribute(name)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
string unimplemented_ordering(ClassObject c, int n) {
|
||||
not c = theObjectType() and
|
||||
string unimplemented_ordering(ClassValue c, int n) {
|
||||
not c = Value::named("object") and
|
||||
not overrides_ordering_method(c, result) and
|
||||
result = ordering_name(n)
|
||||
}
|
||||
|
||||
string unimplemented_ordering_methods(ClassObject c, int n) {
|
||||
string unimplemented_ordering_methods(ClassValue c, int n) {
|
||||
n = 0 and result = "" and exists(unimplemented_ordering(c, _))
|
||||
or
|
||||
exists(string prefix, int nm1 |
|
||||
@@ -58,14 +58,14 @@ string unimplemented_ordering_methods(ClassObject c, int n) {
|
||||
)
|
||||
}
|
||||
|
||||
Object ordering_method(ClassObject c, string name) {
|
||||
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)
|
||||
}
|
||||
|
||||
from ClassObject c, Object ordering, string name
|
||||
where not c.unknowableAttributes() and
|
||||
not total_ordering(c.getPyClass())
|
||||
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, _))
|
||||
|
||||
|
||||
@@ -17,37 +17,39 @@ import python
|
||||
* For numpy arrays, the index may be a list, which are not hashable and needs to be treated specially.
|
||||
*/
|
||||
|
||||
predicate numpy_array_type(ClassObject na) {
|
||||
exists(ModuleObject np | np.getName() = "numpy" or np.getName() = "numpy.core" |
|
||||
na.getAnImproperSuperType() = np.attr("ndarray")
|
||||
predicate numpy_array_type(ClassValue na) {
|
||||
exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" |
|
||||
na.getASuperType() = np.attr("ndarray")
|
||||
)
|
||||
}
|
||||
|
||||
predicate has_custom_getitem(ClassObject cls) {
|
||||
cls.lookupAttribute("__getitem__") instanceof PyFunctionObject
|
||||
predicate has_custom_getitem(Value v) {
|
||||
v.getClass().lookup("__getitem__") instanceof PythonFunctionValue
|
||||
or
|
||||
numpy_array_type(cls)
|
||||
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")
|
||||
}
|
||||
|
||||
predicate unhashable_subscript(ControlFlowNode f, ClassObject c, ControlFlowNode origin) {
|
||||
predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode origin) {
|
||||
is_unhashable(f, c, origin) and
|
||||
exists(SubscriptNode sub | sub.getIndex() = f |
|
||||
exists(ClassObject custom_getitem |
|
||||
sub.getObject().refersTo(_, custom_getitem, _) and
|
||||
exists(Value custom_getitem |
|
||||
sub.getObject().pointsTo(custom_getitem) and
|
||||
not has_custom_getitem(custom_getitem)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate is_unhashable(ControlFlowNode f, ClassObject cls, ControlFlowNode origin) {
|
||||
f.refersTo(_, cls, origin) and
|
||||
(not cls.hasAttribute("__hash__") and not cls.unknowableAttributes() and cls.isNewStyle()
|
||||
or
|
||||
cls.lookupAttribute("__hash__") = theNoneObject()
|
||||
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")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -70,7 +72,7 @@ predicate typeerror_is_caught(ControlFlowNode f) {
|
||||
try.getAHandler().getType().refersTo(theTypeErrorType()))
|
||||
}
|
||||
|
||||
from ControlFlowNode f, ClassObject c, ControlFlowNode origin
|
||||
from ControlFlowNode f, ClassValue c, ControlFlowNode origin
|
||||
where
|
||||
not typeerror_is_caught(f)
|
||||
and
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
import python
|
||||
import Exceptions.NotImplemented
|
||||
|
||||
from Call c, ClassObject t, Expr f, AstNode origin
|
||||
where f = c.getFunc() and f.refersTo(_, t, origin) and
|
||||
not t.isCallable() and not t.unknowableAttributes()
|
||||
and not t.isDescriptorType()
|
||||
and not t = theNoneType()
|
||||
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)
|
||||
|
||||
select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString()
|
||||
|
||||
@@ -384,6 +384,11 @@ class ClassValue extends Value {
|
||||
result = this.(PythonClassObjectInternal).getScope()
|
||||
}
|
||||
|
||||
/** Gets the attribute declared in this class */
|
||||
Value declaredAttribute(string name) {
|
||||
Types::declaredAttribute(this, name, result, _)
|
||||
}
|
||||
|
||||
/** Holds if this class has the attribute `name`, including
|
||||
* attributes declared by super classes.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user