Files
codeql/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql
Taus 5b63b4957c Python: Fix query tests
Mostly just adding `private import LegacyPointsTo`. Sometimes getting
rid of other imports that are superceded by that module.
2025-11-26 12:30:30 +00:00

59 lines
2.2 KiB
Plaintext

/**
* @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 quality
* reliability
* correctness
* @problem.severity warning
* @sub-severity high
* @precision high
* @id py/missing-equals
*/
import python
import Equality
private import LegacyPointsTo
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()
}
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__")
)
}
/**
* Does the non-overridden __eq__ method access the attribute,
* 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()
)
)
}
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())
select cls,
"The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq,
"'__eq__'", store, store.getName()