Skip to content

Commit fccdc30

Browse files
Modernize incomplete ordering query
1 parent 52abf3b commit fccdc30

File tree

1 file changed

+32
-48
lines changed

1 file changed

+32
-48
lines changed

python/ql/src/Classes/IncompleteOrdering.ql

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
* @name Incomplete ordering
33
* @description Class defines one or more ordering method but does not define all 4 ordering comparison methods
44
* @kind problem
5-
* @tags reliability
5+
* @tags quality
6+
* reliability
67
* correctness
78
* @problem.severity warning
89
* @sub-severity low
@@ -11,63 +12,46 @@
1112
*/
1213

1314
import python
15+
import semmle.python.dataflow.new.internal.DataFlowDispatch
16+
import semmle.python.ApiGraphs
1417

15-
predicate total_ordering(Class cls) {
16-
exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering")
17-
or
18-
exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering")
19-
}
20-
21-
string ordering_name(int n) {
22-
result = "__lt__" and n = 1
23-
or
24-
result = "__le__" and n = 2
25-
or
26-
result = "__gt__" and n = 3
27-
or
28-
result = "__ge__" and n = 4
18+
predicate totalOrdering(Class cls) {
19+
cls.getADecorator() =
20+
API::moduleImport("functools").getMember("total_ordering").asSource().asExpr()
2921
}
3022

31-
predicate overrides_ordering_method(ClassValue c, string name) {
32-
name = ordering_name(_) and
33-
(
34-
c.declaresAttribute(name)
35-
or
36-
exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") |
37-
sup.declaresAttribute(name)
38-
)
39-
)
23+
Function getMethod(Class cls, string name) {
24+
result = cls.getAMethod() and
25+
result.getName() = name
4026
}
4127

42-
string unimplemented_ordering(ClassValue c, int n) {
43-
not c = Value::named("object") and
44-
not overrides_ordering_method(c, result) and
45-
result = ordering_name(n)
28+
predicate definesStrictOrdering(Class cls, Function meth) {
29+
meth = getMethod(cls, "__lt__")
30+
or
31+
not exists(getMethod(cls, "__lt__")) and
32+
meth = getMethod(cls, "__gt__")
4633
}
4734

48-
string unimplemented_ordering_methods(ClassValue c, int n) {
49-
n = 0 and result = "" and exists(unimplemented_ordering(c, _))
35+
predicate definesNonStrictOrdering(Class cls, Function meth) {
36+
meth = getMethod(cls, "__le__")
5037
or
51-
exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) |
52-
prefix = "" and result = unimplemented_ordering(c, n)
53-
or
54-
result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5
55-
or
56-
prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n)
57-
)
38+
not exists(getMethod(cls, "__le__")) and
39+
meth = getMethod(cls, "__ge__")
5840
}
5941

60-
Value ordering_method(ClassValue c, string name) {
61-
/* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */
62-
name = ordering_name(_) and result = c.declaredAttribute(name)
42+
predicate missingComparison(Class cls, Function defined, string missing) {
43+
definesStrictOrdering(cls, defined) and
44+
not definesNonStrictOrdering(getADirectSuperclass*(cls), _) and
45+
missing = "__le__ or __ge__"
46+
or
47+
definesNonStrictOrdering(cls, defined) and
48+
not definesStrictOrdering(getADirectSuperclass*(cls), _) and
49+
missing = "__lt__ or __gt__"
6350
}
6451

65-
from ClassValue c, Value ordering, string name
52+
from Class cls, Function defined, string missing
6653
where
67-
not c.failedInference(_) and
68-
not total_ordering(c.getScope()) and
69-
ordering = ordering_method(c, name) and
70-
exists(unimplemented_ordering(c, _))
71-
select c,
72-
"Class " + c.getName() + " implements $@, but does not implement " +
73-
unimplemented_ordering_methods(c, 4) + ".", ordering, name
54+
not totalOrdering(cls) and
55+
missingComparison(cls, defined, missing)
56+
select cls, "This class implements $@, but does not implement an " + missing + " method.", defined,
57+
defined.getName()

0 commit comments

Comments
 (0)