Skip to content

Commit 24b2eb2

Browse files
committed
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.
1 parent f30ebf1 commit 24b2eb2

File tree

1 file changed

+40
-45
lines changed

1 file changed

+40
-45
lines changed

python/ql/src/Functions/SignatureSpecialMethods.ql

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,79 +40,73 @@ predicate is_ternary_op(string name) {
4040

4141
predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" }
4242

43-
int argument_count(PythonFunctionValue f, string name, ClassValue cls) {
44-
cls.declaredAttribute(name) = f and
45-
(
46-
is_unary_op(name) and result = 1
47-
or
48-
is_binary_op(name) and result = 2
49-
or
50-
is_ternary_op(name) and result = 3
51-
or
52-
is_quad_op(name) and result = 4
53-
)
43+
int argument_count(string name) {
44+
is_unary_op(name) and result = 1
45+
or
46+
is_binary_op(name) and result = 2
47+
or
48+
is_ternary_op(name) and result = 3
49+
or
50+
is_quad_op(name) and result = 4
5451
}
5552

5653
predicate incorrect_special_method_defn(
57-
PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner
54+
Function func, string message, boolean show_counts, string name
5855
) {
59-
exists(int required | required = argument_count(func, name, owner) |
56+
exists(int required | required = argument_count(name) |
6057
/* actual_non_default <= actual */
61-
if required > func.maxParameters()
58+
if required > func.getMaxPositionalArguments()
6259
then message = "Too few parameters" and show_counts = true
6360
else
64-
if required < func.minParameters()
61+
if required < func.getMinPositionalArguments()
6562
then message = "Too many parameters" and show_counts = true
6663
else (
67-
func.minParameters() < required and
68-
not func.getScope().hasVarArg() and
69-
message = (required - func.minParameters()) + " default values(s) will never be used" and
64+
func.getMinPositionalArguments() < required and
65+
not func.hasVarArg() and
66+
message =
67+
(required - func.getMinPositionalArguments()) + " default values(s) will never be used" and
7068
show_counts = false
7169
)
7270
)
7371
}
7472

75-
predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
76-
owner.declaredAttribute("__pow__") = func and
73+
predicate incorrect_pow(Function func, string message, boolean show_counts) {
7774
(
78-
func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true
75+
func.getMaxPositionalArguments() < 2 and message = "Too few parameters" and show_counts = true
7976
or
80-
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
77+
func.getMinPositionalArguments() > 3 and message = "Too many parameters" and show_counts = true
8178
or
82-
func.minParameters() < 2 and
83-
message = (2 - func.minParameters()) + " default value(s) will never be used" and
79+
func.getMinPositionalArguments() < 2 and
80+
message = (2 - func.getMinPositionalArguments()) + " default value(s) will never be used" and
8481
show_counts = false
8582
or
86-
func.minParameters() = 3 and
83+
func.getMinPositionalArguments() = 3 and
8784
message = "Third parameter to __pow__ should have a default value" and
8885
show_counts = false
8986
)
9087
}
9188

92-
predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) {
93-
owner.declaredAttribute("__get__") = func and
89+
predicate incorrect_get(Function func, string message, boolean show_counts) {
9490
(
95-
func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true
91+
func.getMaxPositionalArguments() < 3 and message = "Too few parameters" and show_counts = true
9692
or
97-
func.minParameters() > 3 and message = "Too many parameters" and show_counts = true
93+
func.getMinPositionalArguments() > 3 and message = "Too many parameters" and show_counts = true
9894
or
99-
func.minParameters() < 2 and
100-
not func.getScope().hasVarArg() and
101-
message = (2 - func.minParameters()) + " default value(s) will never be used" and
95+
func.getMinPositionalArguments() < 2 and
96+
not func.hasVarArg() and
97+
message = (2 - func.getMinPositionalArguments()) + " default value(s) will never be used" and
10298
show_counts = false
10399
)
104100
}
105101

106-
string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) {
107-
exists(int i | i = argument_count(f, name, owner) | result = i.toString())
108-
or
109-
owner.declaredAttribute(name) = f and
110-
(name = "__get__" or name = "__pow__") and
111-
result = "2 or 3"
102+
string should_have_parameters(string name) {
103+
if name in ["__pow__", "__get__"]
104+
then result = "2 or 3"
105+
else result = argument_count(name).toString()
112106
}
113107

114-
string has_parameters(PythonFunctionValue f) {
115-
exists(int i | i = f.minParameters() |
108+
string has_parameters(Function f) {
109+
exists(int i | i = f.getMinPositionalArguments() |
116110
i = 0 and result = "no parameters"
117111
or
118112
i = 1 and result = "1 parameter"
@@ -125,19 +119,20 @@ from
125119
PythonFunctionValue f, string message, string sizes, boolean show_counts, string name,
126120
ClassValue owner
127121
where
122+
owner.declaredAttribute(name) = f and
128123
(
129-
incorrect_special_method_defn(f, message, show_counts, name, owner)
124+
incorrect_special_method_defn(f.getScope(), message, show_counts, name)
130125
or
131-
incorrect_pow(f, message, show_counts, owner) and name = "__pow__"
126+
incorrect_pow(f.getScope(), message, show_counts) and name = "__pow__"
132127
or
133-
incorrect_get(f, message, show_counts, owner) and name = "__get__"
128+
incorrect_get(f.getScope(), message, show_counts) and name = "__get__"
134129
) and
135130
(
136131
show_counts = false and sizes = ""
137132
or
138133
show_counts = true and
139134
sizes =
140-
", which has " + has_parameters(f) + ", but should have " +
141-
should_have_parameters(f, name, owner)
135+
", which has " + has_parameters(f.getScope()) + ", but should have " +
136+
should_have_parameters(name)
142137
)
143138
select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName()

0 commit comments

Comments
 (0)