Python: Add support for PEP-758 exception syntax

See https://peps.python.org/pep-0758/ for more details.

We implement this by extending the syntax for exceptions and exception
groups so that the `type` field can now contain either an expression
(which matches the old behaviour), or a comma-separated list of at least
two elements (representing the new behaviour).

We model the latter case using a new node type `exception_list`, which
in `tsg-python` is simply mapped to a tuple. This means it matches the
existing behaviour (when the tuple is surrounded by parentheses)
exactly, hence we don't need to change any other code.

As a consequence of this, however, we cannot directly parse the Python
2.7 syntax `except Foo, e: ...` as `except Foo as e: ...`, as this would
introduce an ambiguity in the grammar. Thus, we have removed support for
the (deprecated) 2.7-style syntax, and only allow `as` to indicate
binding of the exception. The syntax `except Foo, e: ...` continues to
be parsed (in particular, it's not suddenly a syntax error), but it will
be parsed as if it were `except (Foo, e): ...`, which may not give the
correct results.

In principle we could extend the QL libraries to account for this case
(specifically when analysing Python 2 code). In practice, however, I
expect this to have a minor impact on results, and not worth the
additional investment at this time.
This commit is contained in:
Taus
2025-12-08 17:09:40 +00:00
parent a35fba1e36
commit 6ba65b0dd2
2 changed files with 17 additions and 4 deletions

View File

@@ -12,7 +12,7 @@
(assignment !type) @assign
{ let @assign.node = (ast-node @assign "Assign") }
[ (expression_list) (tuple) (tuple_pattern) (pattern_list) (index_expression_list) ] @tuple
[ (expression_list) (tuple) (tuple_pattern) (pattern_list) (index_expression_list) (exception_list)] @tuple
{ let @tuple.node = (ast-node @tuple "Tuple") }
(list_pattern) @list
@@ -3445,6 +3445,9 @@
(tuple element: (_) @elt) @parent
(tuple_pattern element: (_) @elt) @parent
; An exception list, as in `except A, B, C: ...`
(exception_list element: (_) @elt) @parent
]
{
edge @parent.node -> @elt.node
@@ -3480,6 +3483,7 @@
(parenthesized_expression inner: (_) @elt)
(set element: (_) @elt)
(match_sequence_pattern (_) @elt)
(exception_list element: (_) @elt)
] @seq
{
attr (@elt.node) _inherited_ctx = @seq.node

View File

@@ -297,12 +297,21 @@ module.exports = grammar({
)
),
exception_list: $ => seq(
field('element', $.expression),
repeat1(
seq(
',',
field('element', $.expression))
)
),
except_clause: $ => seq(
'except',
optional(seq(
field('type', $.expression),
field('type', choice($.expression, $.exception_list)),
optional(seq(
choice('as', ','),
'as',
field('alias', $.expression)
))
)),
@@ -314,7 +323,7 @@ module.exports = grammar({
'except',
'*',
seq(
field('type', $.expression),
field('type', choice($.expression, $.exception_list)),
optional(seq(
'as',
field('alias', $.expression)