Python: Refactor special method query

Moves a bunch of `owner.declaredAttribute(name) = f` instances to the
top level, in the process greatly cleaning up the code. The behaviour
should be the unchanged.

Having done this, there's only one place where we depend on points-to,
and that's in the remaining `declaredAttribute` call. This should
greatly simplify the move away from points to.
This commit is contained in:
Taus
2025-03-07 17:23:38 +00:00
parent f30ebf1571
commit 24b2eb24c1

View File

@@ -40,79 +40,73 @@ predicate is_ternary_op(string name) {
predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" }
int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
cls.declaredAttribute(name) = f and
(
is_unary_op(name) and result = 1
or
is_binary_op(name) and result = 2
or
is_ternary_op(name) and result = 3
or
is_quad_op(name) and result = 4
)
int argument_count(string name) {
is_unary_op(name) and result = 1
or
is_binary_op(name) and result = 2
or
is_ternary_op(name) and result = 3
or
is_quad_op(name) and result = 4
}
predicate incorrect_special_method_defn(
PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner
Function func, string message, boolean show_counts, string name
) {
exists(int required | required = argument_count(func, name, owner) |
exists(int required | required = argument_count(name) |
/* actual_non_default <= actual */
if required > func.maxParameters()
if required > func.getMaxPositionalArguments()
then message = "Too few parameters" and show_counts = true
else
if required < func.minParameters()
if required < func.getMinPositionalArguments()
then message = "Too many parameters" and show_counts = true
else (
func.minParameters() < required and
not func.getScope().hasVarArg() and
message = (required - func.minParameters()) + " default values(s) will never be used" and
func.getMinPositionalArguments() < required and
not func.hasVarArg() and
message =
(required - func.getMinPositionalArguments()) + " default values(s) will never be used" and
show_counts = false
)
)
}
predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
owner.declaredAttribute("__pow__") = func and
predicate incorrect_pow(Function func, string message, boolean show_counts) {
(
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
func.getMaxPositionalArguments() < 2 and message = "Too few parameters" and show_counts = true
or
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
func.getMinPositionalArguments() > 3 and message = "Too many parameters" and show_counts = true
or
func.minParameters() < 2 and
message = (2 - func.minParameters()) + " default value(s) will never be used" and
func.getMinPositionalArguments() < 2 and
message = (2 - func.getMinPositionalArguments()) + " default value(s) will never be used" and
show_counts = false
or
func.minParameters() = 3 and
func.getMinPositionalArguments() = 3 and
message = "Third parameter to __pow__ should have a default value" and
show_counts = false
)
}
predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
owner.declaredAttribute("__get__") = func and
predicate incorrect_get(Function func, string message, boolean show_counts) {
(
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
func.getMaxPositionalArguments() < 3 and message = "Too few parameters" and show_counts = true
or
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
func.getMinPositionalArguments() > 3 and message = "Too many parameters" and show_counts = true
or
func.minParameters() < 2 and
not func.getScope().hasVarArg() and
message = (2 - func.minParameters()) + " default value(s) will never be used" and
func.getMinPositionalArguments() < 2 and
not func.hasVarArg() and
message = (2 - func.getMinPositionalArguments()) + " default value(s) will never be used" and
show_counts = false
)
}
string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) {
exists(int i | i = argument_count(f, name, owner) | result = i.toString())
or
owner.declaredAttribute(name) = f and
(name = "__get__" or name = "__pow__") and
result = "2 or 3"
string should_have_parameters(string name) {
if name in ["__pow__", "__get__"]
then result = "2 or 3"
else result = argument_count(name).toString()
}
string has_parameters(PythonFunctionValue f) {
exists(int i | i = f.minParameters() |
string has_parameters(Function f) {
exists(int i | i = f.getMinPositionalArguments() |
i = 0 and result = "no parameters"
or
i = 1 and result = "1 parameter"
@@ -125,19 +119,20 @@ from
PythonFunctionValue f, string message, string sizes, boolean show_counts, string name,
ClassValue owner
where
owner.declaredAttribute(name) = f and
(
incorrect_special_method_defn(f, message, show_counts, name, owner)
incorrect_special_method_defn(f.getScope(), message, show_counts, name)
or
incorrect_pow(f, message, show_counts, owner) and name = "__pow__"
incorrect_pow(f.getScope(), message, show_counts) and name = "__pow__"
or
incorrect_get(f, message, show_counts, owner) and name = "__get__"
incorrect_get(f.getScope(), message, show_counts) and name = "__get__"
) and
(
show_counts = false and sizes = ""
or
show_counts = true and
sizes =
", which has " + has_parameters(f) + ", but should have " +
should_have_parameters(f, name, owner)
", which has " + has_parameters(f.getScope()) + ", but should have " +
should_have_parameters(name)
)
select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName()