Skip to content

Commit bca03d1

Browse files
committed
Python: Port IllegalRaise.ql
Adds a convenient way to get the class name for an immutable literal (to maintain the same output format as was provided by the points-to version). I don't know if people are in the habit of writing `raise 5`, but I guess `raise "NotImplemented"` (wrong on so many levels) is not entirely impossible. No test changes.
1 parent 1593cbf commit bca03d1

File tree

2 files changed

+60
-10
lines changed

2 files changed

+60
-10
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,23 @@ module DuckTyping {
21132113
or
21142114
f.getADecorator().(Name).getId() = "property"
21152115
}
2116+
2117+
/** Gets the name of the builtin class of the immutable literal `lit`. */
2118+
string getClassName(ImmutableLiteral lit) {
2119+
lit instanceof IntegerLiteral and result = "int"
2120+
or
2121+
lit instanceof FloatLiteral and result = "float"
2122+
or
2123+
lit instanceof ImaginaryLiteral and result = "complex"
2124+
or
2125+
lit instanceof NegativeIntegerLiteral and result = "int"
2126+
or
2127+
lit instanceof StringLiteral and result = "str"
2128+
or
2129+
lit instanceof BooleanLiteral and result = "bool"
2130+
or
2131+
lit instanceof None and result = "NoneType"
2132+
}
21162133
}
21172134

21182135
/**

python/ql/src/Exceptions/IllegalRaise.ql

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,48 @@
1212
*/
1313

1414
import python
15-
import Raising
16-
import Exceptions.NotImplemented
17-
private import LegacyPointsTo
15+
import semmle.python.dataflow.new.internal.DataFlowDispatch
16+
import semmle.python.ApiGraphs
17+
private import ExceptionTypes
1818

19-
from Raise r, ClassValue t
19+
/**
20+
* Holds if `r` raises an instance of a builtin non-exception class named `name`.
21+
*/
22+
private predicate raisesNonExceptionBuiltin(Raise r, string name) {
23+
exists(Expr raised | raised = r.getRaised() |
24+
API::builtin(name).getAValueReachableFromSource().asExpr() = raised
25+
or
26+
API::builtin(name).getAValueReachableFromSource().asExpr() = raised.(Call).getFunc() and
27+
// Exclude `type` since `type(x)` returns the class of `x`, not a `type` instance
28+
not name = "type"
29+
) and
30+
not builtinException(name)
31+
}
32+
33+
from Raise r, string msg
2034
where
21-
type_or_typeof(r, t, _) and
22-
not t.isLegalExceptionType() and
23-
not t.failedInference(_) and
24-
not use_of_not_implemented_in_raise(r, _)
25-
select r,
26-
"Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead."
35+
not raisesNonExceptionBuiltin(r, "NotImplemented") and
36+
(
37+
exists(ExceptType t |
38+
t.isRaisedBy(r) and
39+
not t.isLegalExceptionType() and
40+
not t.getName() = "None" and
41+
msg =
42+
"Illegal class '" + t.getName() +
43+
"' raised; will result in a TypeError being raised instead."
44+
)
45+
or
46+
exists(ImmutableLiteral lit | lit = r.getRaised() |
47+
msg =
48+
"Illegal class '" + DuckTyping::getClassName(lit) +
49+
"' raised; will result in a TypeError being raised instead."
50+
)
51+
or
52+
exists(string name |
53+
raisesNonExceptionBuiltin(r, name) and
54+
not r.getRaised() instanceof ImmutableLiteral and
55+
not name = "None" and
56+
msg = "Illegal class '" + name + "' raised; will result in a TypeError being raised instead."
57+
)
58+
)
59+
select r, msg

0 commit comments

Comments
 (0)