Skip to content

Commit 8bdf680

Browse files
Add qldoc
1 parent 3525e83 commit 8bdf680

File tree

1 file changed

+18
-1
lines changed

1 file changed

+18
-1
lines changed

python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ import python
1515
import semmle.python.ApiGraphs
1616
import semmle.python.dataflow.new.internal.DataFlowDispatch
1717

18+
/** Holds if `name` is the name of a special method for attribute access such as `a.b`, that should raise an `AttributeError`. */
1819
private predicate attributeMethod(string name) {
1920
name = ["__getattribute__", "__getattr__", "__delattr__"] // __setattr__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
2021
}
2122

23+
/** Holds if `name` is the name of a special method for indexing operations such as `a[b]`, that should raise a `LookupError`. */
2224
private predicate indexingMethod(string name) {
2325
name = ["__getitem__", "__delitem__"] // __setitem__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
2426
}
2527

28+
/** Holds if `name` is the name of a special method for arithmetic operations. */
2629
private predicate arithmeticMethod(string name) {
2730
name =
2831
[
@@ -35,6 +38,7 @@ private predicate arithmeticMethod(string name) {
3538
]
3639
}
3740

41+
/** Holds if `name is the name of a special method for ordering operations such as `a < b`. */
3842
private predicate orderingMethod(string name) {
3943
name =
4044
[
@@ -45,6 +49,7 @@ private predicate orderingMethod(string name) {
4549
]
4650
}
4751

52+
/** Holds if `name` is the name of a special method for casting an object to a numeric type, such as `int(x)` */
4853
private predicate castMethod(string name) {
4954
name =
5055
[
@@ -53,9 +58,10 @@ private predicate castMethod(string name) {
5358
"__index__",
5459
"__trunc__",
5560
"__complex__"
56-
]
61+
] // __bool__ excluded as it makes sense to allow it to always raise
5762
}
5863

64+
/** Holds if we allow a special method named `name` to raise `exec` as an exception. */
5965
predicate correctRaise(string name, Expr exec) {
6066
execIsOfType(exec, "TypeError") and
6167
(
@@ -71,6 +77,7 @@ predicate correctRaise(string name, Expr exec) {
7177
)
7278
}
7379

80+
/** Holds if it is preferred for `name` to raise exceptions of type `execName`. `message` is the alert message. */
7481
predicate preferredRaise(string name, string execName, string message) {
7582
attributeMethod(name) and
7683
execName = "AttributeError" and
@@ -93,6 +100,7 @@ predicate preferredRaise(string name, string execName, string message) {
93100
message = "should raise a TypeError instead."
94101
}
95102

103+
/** Holds if `exec` is an exception object of the type named `execName`. */
96104
predicate execIsOfType(Expr exec, string execName) {
97105
// Might make sense to have execName be an IPA type here. Or part of a more general API modeling builtin/stdlib subclass relations.
98106
exists(string subclass |
@@ -114,6 +122,10 @@ predicate execIsOfType(Expr exec, string execName) {
114122
)
115123
}
116124

125+
/**
126+
* Holds if `meth` need not be implemented if it always raises. `message` is the alert message, and `allowNotImplemented` is true
127+
* if we still allow the method to always raise `NotImplementedError`.
128+
*/
117129
predicate noNeedToAlwaysRaise(Function meth, string message, boolean allowNotImplemented) {
118130
meth.getName() = "__hash__" and
119131
message = "use __hash__ = None instead." and
@@ -130,14 +142,17 @@ predicate noNeedToAlwaysRaise(Function meth, string message, boolean allowNotImp
130142
)
131143
}
132144

145+
/** Holds if `func` has a decorator likely marking it as an abstract method. */
133146
predicate isAbstract(Function func) { func.getADecorator().(Name).getId().matches("%abstract%") }
134147

148+
/** Holds if `f` always raises the exception `exec`. */
135149
predicate alwaysRaises(Function f, Expr exec) {
136150
directlyRaises(f, exec) and
137151
strictcount(Expr e | directlyRaises(f, e)) = 1 and
138152
not exists(f.getANormalExit())
139153
}
140154

155+
/** Holds if `f` directly raises `expr` using a `raise` statement. */
141156
predicate directlyRaises(Function f, Expr exec) {
142157
exists(Raise r |
143158
r.getScope() = f and
@@ -146,10 +161,12 @@ predicate directlyRaises(Function f, Expr exec) {
146161
)
147162
}
148163

164+
/** Holds if `exec` is a `NotImplementedError`. */
149165
predicate isNotImplementedError(Expr exec) {
150166
exec = API::builtin("NotImplementedError").getACall().asExpr()
151167
}
152168

169+
/** Gets the name of the builtin exception type `exec` constructs, if it can be determined. */
153170
string getExecName(Expr exec) { result = exec.(Call).getFunc().(Name).getId() }
154171

155172
from Function f, Expr exec, string message

0 commit comments

Comments
 (0)