Skip to content

Commit e2eb69c

Browse files
committed
Python: Port IllegalExceptionHandlerType.ql
A few relevant changes compared to the points-to version: - we've lost `origin`, so we can no longer point to where the illegal type lives. I opted to keep the output message the same, mirroring what we were already doing in IllegalRaise.ql. - We no longer track literal values flowing in from elsewhere, so we lost a single test result where the handled "type" is the result of calling a float-returning function. Apart from that, the only test changes are cosmetic.
1 parent c4ec331 commit e2eb69c

File tree

2 files changed

+35
-18
lines changed

2 files changed

+35
-18
lines changed

python/ql/src/Exceptions/IllegalExceptionHandlerType.ql

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,38 @@
1212
*/
1313

1414
import python
15-
private import LegacyPointsTo
15+
import semmle.python.dataflow.new.internal.DataFlowDispatch
16+
private import ExceptionTypes
1617

17-
from ExceptFlowNodeWithPointsTo ex, Value t, ClassValue c, ControlFlowNode origin, string what
18+
/**
19+
* Gets an expression used as a handler type in the `except` clause at `ex`,
20+
* either directly or as an element of a tuple.
21+
*/
22+
Expr handlerExpr(ExceptStmt ex) {
23+
result = ex.getType() or
24+
result = ex.getType().(Tuple).getAnElt()
25+
}
26+
27+
/**
28+
* Gets an exception type used in the `except` clause at `ex`,
29+
* where that type is not a legal exception type.
30+
*/
31+
ExceptType illegalHandlerType(ExceptStmt ex) {
32+
result.getAUse().asExpr() = handlerExpr(ex) and
33+
not result.isLegalExceptionType()
34+
}
35+
36+
from ExceptStmt ex, string msg
1837
where
19-
ex.handledException(t, c, origin) and
20-
(
21-
exists(ClassValue x | x = t |
22-
not x.isLegalExceptionType() and
23-
not x.failedInference(_) and
24-
what = "class '" + x.getName() + "'"
25-
)
26-
or
27-
not t instanceof ClassValue and
28-
what = "instance of '" + c.getName() + "'"
38+
exists(ExceptType t | t = illegalHandlerType(ex) |
39+
msg =
40+
"Non-exception class '" + t.getName() +
41+
"' in exception handler which will never match raised exception."
42+
)
43+
or
44+
exists(ImmutableLiteral lit | lit = handlerExpr(ex) and not lit instanceof None |
45+
msg =
46+
"Non-exception class '" + DuckTyping::getClassName(lit) +
47+
"' in exception handler which will never match raised exception."
2948
)
30-
select ex.getNode(),
31-
"Non-exception $@ in exception handler which will never match raised exception.", origin, what
49+
select ex, msg
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
| exceptions_test.py:51:5:51:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:33:1:33:28 | ControlFlowNode for ClassExpr | class 'NotException1' |
2-
| exceptions_test.py:54:5:54:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:36:1:36:28 | ControlFlowNode for ClassExpr | class 'NotException2' |
3-
| exceptions_test.py:138:5:138:22 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:133:12:133:14 | ControlFlowNode for FloatLiteral | instance of 'float' |
4-
| pypy_test.py:14:5:14:14 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | pypy_test.py:14:12:14:13 | ControlFlowNode for IntegerLiteral | instance of 'int' |
1+
| exceptions_test.py:51:5:51:25 | ExceptStmt | Non-exception class 'NotException1' in exception handler which will never match raised exception. |
2+
| exceptions_test.py:54:5:54:25 | ExceptStmt | Non-exception class 'NotException2' in exception handler which will never match raised exception. |
3+
| pypy_test.py:14:5:14:14 | ExceptStmt | Non-exception class 'int' in exception handler which will never match raised exception. |

0 commit comments

Comments
 (0)