Files
codeql/python/ql/src/Statements/StatementNoEffect.ql
Taus Brock-Nannestad f07a7bf8cf 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.
2020-07-07 15:43:52 +02:00

138 lines
4.0 KiB
Plaintext

/**
* @name Statement has no effect
* @description A statement has no effect
* @kind problem
* @tags maintainability
* useless-code
* external/cwe/cwe-561
* @problem.severity recommendation
* @sub-severity high
* @precision high
* @id py/ineffectual-statement
*/
import python
predicate understood_attribute(Attribute attr, ClassValue cls, ClassValue attr_cls) {
exists(string name | attr.getName() = name |
attr.getObject().pointsTo().getClass() = cls and
cls.attr(name).getClass() = attr_cls
)
}
/* Conservative estimate of whether attribute lookup has a side effect */
predicate side_effecting_attribute(Attribute attr) {
exists(ClassValue cls, ClassValue attr_cls |
understood_attribute(attr, cls, attr_cls) and
side_effecting_descriptor_type(attr_cls)
)
}
predicate maybe_side_effecting_attribute(Attribute attr) {
not understood_attribute(attr, _, _) and not attr.pointsTo(_)
or
side_effecting_attribute(attr)
}
predicate side_effecting_descriptor_type(ClassValue descriptor) {
descriptor.isDescriptorType() and
// Technically all descriptor gets have side effects,
// but some are indicative of a missing call and
// we want to treat them as having no effect.
not descriptor = ClassValue::functionType() and
not descriptor = ClassValue::staticmethod() and
not descriptor = ClassValue::classmethod()
}
/**
* Side effecting binary operators are rare, so we assume they are not
* side-effecting unless we know otherwise.
*/
predicate side_effecting_binary(Expr b) {
exists(Expr sub, ClassValue cls, string method_name |
binary_operator_special_method(b, sub, cls, method_name)
or
comparison_special_method(b, sub, cls, method_name)
|
method_name = special_method() and
cls.hasAttribute(method_name) and
not exists(ClassValue declaring |
declaring.declaresAttribute(method_name) and
declaring = cls.getASuperType() and
declaring.isBuiltin() and
not declaring = ClassValue::object()
)
)
}
pragma[nomagic]
private predicate binary_operator_special_method(
BinaryExpr b, Expr sub, ClassValue cls, string method_name
) {
method_name = special_method() and
sub = b.getLeft() and
method_name = b.getOp().getSpecialMethodName() and
sub.pointsTo().getClass() = cls
}
pragma[nomagic]
private predicate comparison_special_method(Compare b, Expr sub, ClassValue cls, string method_name) {
exists(Cmpop op |
b.compares(sub, op, _) and
method_name = op.getSpecialMethodName()
) and
sub.pointsTo().getClass() = cls
}
private string special_method() {
result = any(Cmpop c).getSpecialMethodName()
or
result = any(BinaryExpr b).getOp().getSpecialMethodName()
}
predicate is_notebook(File f) {
exists(Comment c | c.getLocation().getFile() = f |
c.getText().regexpMatch("#\\s*<nbformat>.+</nbformat>\\s*")
)
}
/** Expression (statement) in a jupyter/ipython notebook */
predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) }
FunctionValue assertRaises() {
result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises")
}
/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */
predicate in_raises_test(Expr e) {
exists(With w |
w.contains(e) and
w.getContextExpr() = assertRaises().getACall().getNode()
)
}
/** Holds if expression has the form of a Python 2 `print >> out, ...` statement */
predicate python2_print(Expr e) {
e.(BinaryExpr).getLeft().(Name).getId() = "print" and
e.(BinaryExpr).getOp() instanceof RShift
or
python2_print(e.(Tuple).getElt(0))
}
predicate no_effect(Expr e) {
// strings can be used as comments
not e instanceof StrConst and
not e.hasSideEffects() and
forall(Expr sub | sub = e.getASubExpression*() |
not side_effecting_binary(sub) and
not maybe_side_effecting_attribute(sub)
) and
not in_notebook(e) and
not in_raises_test(e) and
not python2_print(e)
}
from ExprStmt stmt
where no_effect(stmt.getValue())
select stmt, "This statement has no effect."