Python: Fix bug in handling of **kwargs in class bases

This caused a dataset check error on the `python/cpython` database, as
we had a `DictUnpacking` node whose parent was not a `dict_item_list`,
but rather an `expr_list`.

Investigating a bit further revealed that this was because in a
construction like

```python
class C[T](base, foo=bar, **kwargs): ...
```
we were mistakenly adding `**kwargs` to the same list as `base` (which
is just a list of expressions), rather than the same list as `foo=bar`
(which is a list of dictionary items)

The ultimate cause of this was the use of `! name` in `python.tsg` to
distinguish between bases and keyword arguments (only the latter of
which have the `name` field). Because `dictionary_splat` doesn't have a
`name` field either, these were mistakenly put in the wrong list,
leading to the error.

Also, because our previous test of `class` statements did not include a
`**kwargs` construction, we were not checking that the new parser
behaved correctly in this case. For the most part this was not a
problem, but on files that use syntax not supported by the old parser
(like type parameters on classes), this became an issue. This is also
why we did not see this error previously.

To fix this, we added `! value` (which is a field present on
`dictionary_splat` nodes) as a secondary filter, and added a third
stanza to handle `dictionary_splat` nodes.
This commit is contained in:
Taus
2024-10-18 12:22:17 +00:00
parent f3abe549f3
commit 1cd04c96c7

View File

@@ -1277,16 +1277,18 @@
attr (@class.inner_scope -> @stmt.node) body = (named-child-index @stmt) attr (@class.inner_scope -> @stmt.node) body = (named-child-index @stmt)
} }
; Class.bases - using `(_ !name)` as a proxy for all non-keyword arguments. ; Class.bases - using `(_ !value !name)` as a proxy for all non-keyword arguments.
; In particular, `keyword_argument` nodes have a `name` field, and `dictionary_splat`
; nodes have a `value` field.
(class_definition (class_definition
superclasses: (argument_list element: (_ !name) @arg) superclasses: (argument_list element: (_ !value !name) @arg)
) @class ) @class
{ {
edge @class.class_expr -> @arg.node edge @class.class_expr -> @arg.node
attr (@class.class_expr -> @arg.node) bases = (named-child-index @arg) attr (@class.class_expr -> @arg.node) bases = (named-child-index @arg)
} }
; Class.keywords ; Class.keywords of the form `foo=bar`
(class_definition (class_definition
superclasses: (argument_list element: (keyword_argument) @arg) superclasses: (argument_list element: (keyword_argument) @arg)
) @class ) @class
@@ -1295,6 +1297,15 @@
attr (@class.class_expr -> @arg.node) keywords = (named-child-index @arg) attr (@class.class_expr -> @arg.node) keywords = (named-child-index @arg)
} }
; Class.keywords of the form `**kwargs`
(class_definition
superclasses: (argument_list element: (dictionary_splat) @arg)
) @class
{
edge @class.class_expr -> @arg.node
attr (@class.class_expr -> @arg.node) keywords = (named-child-index @arg)
}
;;;;;; End of Class ;;;;;; End of Class
;;;;;; Assign statements ;;;;;; Assign statements