/** * @name Useless class * @description Class only defines one public method (apart from `__init__` or `__new__`) and should be replaced by a function * @kind problem * @tags maintainability * useless-code * @problem.severity recommendation * @sub-severity low * @precision medium * @id py/useless-class */ 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()) } predicate does_not_define_special_method(Class cls) { not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) } predicate no_inheritance(Class c) { not exists(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) ) 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 in ["pop", "remove", "discard", "extend", "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) } 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." ) select c, msg