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.
This commit is contained in:
Taus
2026-02-24 22:24:19 +00:00
parent 4606d904ce
commit c4ec331e96
2 changed files with 60 additions and 10 deletions

View File

@@ -2134,6 +2134,23 @@ module DuckTyping {
or
f.getADecorator().(Name).getId() = "property"
}
/** Gets the name of the builtin class of the immutable literal `lit`. */
string getClassName(ImmutableLiteral lit) {
lit instanceof IntegerLiteral and result = "int"
or
lit instanceof FloatLiteral and result = "float"
or
lit instanceof ImaginaryLiteral and result = "complex"
or
lit instanceof NegativeIntegerLiteral and result = "int"
or
lit instanceof StringLiteral and result = "str"
or
lit instanceof BooleanLiteral and result = "bool"
or
lit instanceof None and result = "NoneType"
}
}
/**

View File

@@ -12,15 +12,48 @@
*/
import python
import Raising
import Exceptions.NotImplemented
private import LegacyPointsTo
import semmle.python.dataflow.new.internal.DataFlowDispatch
import semmle.python.ApiGraphs
private import ExceptionTypes
from Raise r, ClassValue t
/**
* Holds if `r` raises an instance of a builtin non-exception class named `name`.
*/
private predicate raisesNonExceptionBuiltin(Raise r, string name) {
exists(Expr raised | raised = r.getRaised() |
API::builtin(name).getAValueReachableFromSource().asExpr() = raised
or
API::builtin(name).getAValueReachableFromSource().asExpr() = raised.(Call).getFunc() and
// Exclude `type` since `type(x)` returns the class of `x`, not a `type` instance
not name = "type"
) and
not builtinException(name)
}
from Raise r, string msg
where
type_or_typeof(r, t, _) and
not t.isLegalExceptionType() and
not t.failedInference(_) and
not use_of_not_implemented_in_raise(r, _)
select r,
"Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead."
not raisesNonExceptionBuiltin(r, "NotImplemented") and
(
exists(ExceptType t |
t.isRaisedBy(r) and
not t.isLegalExceptionType() and
not t.getName() = "None" and
msg =
"Illegal class '" + t.getName() +
"' raised; will result in a TypeError being raised instead."
)
or
exists(ImmutableLiteral lit | lit = r.getRaised() |
msg =
"Illegal class '" + DuckTyping::getClassName(lit) +
"' raised; will result in a TypeError being raised instead."
)
or
exists(string name |
raisesNonExceptionBuiltin(r, name) and
not r.getRaised() instanceof ImmutableLiteral and
not name = "None" and
msg = "Illegal class '" + name + "' raised; will result in a TypeError being raised instead."
)
)
select r, msg