mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Initial commit of Python queries and QL libraries.
This commit is contained in:
committed by
Mark Shannon
parent
90c75cd362
commit
5f58824d1b
52
python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql
Normal file
52
python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @name __eq__ not overridden when adding attributes
|
||||
* @description When adding new attributes to instances of a class, equality for that class needs to be defined.
|
||||
* @kind problem
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* @problem.severity warning
|
||||
* @sub-severity high
|
||||
* @precision high
|
||||
* @id py/missing-equals
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.SelfAttribute
|
||||
import Equality
|
||||
|
||||
predicate class_stores_to_attribute(ClassObject cls, SelfAttributeStore store, string name) {
|
||||
exists(FunctionObject f | f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name) and
|
||||
/* Exclude classes used as metaclasses */
|
||||
not cls.getASuperType() = theTypeType()
|
||||
}
|
||||
|
||||
predicate should_override_eq(ClassObject cls, Object base_eq) {
|
||||
not cls.declaresAttribute("__eq__") and
|
||||
exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
|
||||
not exists(GenericEqMethod eq | eq.getScope() = sup.getPyClass()) and
|
||||
not exists(IdentityEqMethod eq | eq.getScope() = sup.getPyClass()) and
|
||||
not base_eq.(FunctionObject).getFunction() instanceof IdentityEqMethod and
|
||||
not base_eq = theObjectType().declaredAttribute("__eq__")
|
||||
)
|
||||
}
|
||||
|
||||
/** Does the non-overridden __eq__ method access the attribute,
|
||||
* which implies that the __eq__ method does not need to be overridden.
|
||||
*/
|
||||
predicate superclassEqExpectsAttribute(ClassObject cls, PyFunctionObject base_eq, string attrname) {
|
||||
not cls.declaresAttribute("__eq__") and
|
||||
exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq |
|
||||
exists(SelfAttributeRead store |
|
||||
store.getName() = attrname |
|
||||
store.getScope() = base_eq.getFunction()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from ClassObject cls, SelfAttributeStore store, Object base_eq
|
||||
where class_stores_to_attribute(cls, store, _) and should_override_eq(cls, base_eq) and
|
||||
/* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */
|
||||
not cls.getASuperType().getName() = "TestCase" and
|
||||
not superclassEqExpectsAttribute(cls, base_eq, store.getName())
|
||||
|
||||
select cls, "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, "'__eq__'", store, store.getName()
|
||||
Reference in New Issue
Block a user