Skip to content

Commit c4ec331

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 4606d90 commit c4ec331

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
@@ -2134,6 +2134,23 @@ module DuckTyping {
21342134
or
21352135
f.getADecorator().(Name).getId() = "property"
21362136
}
2137+
2138+
/** Gets the name of the builtin class of the immutable literal `lit`. */
2139+
string getClassName(ImmutableLiteral lit) {
2140+
lit instanceof IntegerLiteral and result = "int"
2141+
or
2142+
lit instanceof FloatLiteral and result = "float"
2143+
or
2144+
lit instanceof ImaginaryLiteral and result = "complex"
2145+
or
2146+
lit instanceof NegativeIntegerLiteral and result = "int"
2147+
or
2148+
lit instanceof StringLiteral and result = "str"
2149+
or
2150+
lit instanceof BooleanLiteral and result = "bool"
2151+
or
2152+
lit instanceof None and result = "NoneType"
2153+
}
21372154
}
21382155

21392156
/**

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)