Repository: Instagram/LibCST (language: python)

Issue:
Fails to parse parenthesized `with` statement without trailing comma
#584 added parenthesized `with` statements to the native parser, but they’re only accepted when a trailing comma is present. Python accepts them with or without a trailing comma.

```console
$ pip freeze
libcst==0.4.9
mypy-extensions==0.4.3
PyYAML==6.0
typing-inspect==0.8.0
typing_extensions==4.4.0

$ export LIBCST_PARSER_TYPE=native

$ python3 -c 'import libcst; libcst.parse_statement("with (a as a, b as b,): pass")'

$ python3 -c 'import libcst; libcst.parse_statement("with (a as a, b as b): pass")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/tmp/v/lib/python3.10/site-packages/libcst/_parser/entrypoints.py", line 138, in parse_statement
    result = _parse(
  File "/tmp/v/lib/python3.10/site-packages/libcst/_parser/entrypoints.py", line 55, in _parse
    return parse(source_str)
libcst._exceptions.ParserSyntaxError: Syntax Error @ 1:1.
parser error: error at 1:20: expected *

with (a as a, b as b): pass
^
```

Cc @stroxler


Discussion / hints:
~~I'm hitting this as well in https://github.com/Zac-HD/flake8-trio
It additionally also fails with newlines:~~
nvm, I didn't set the parser type.
Thanks for the bug reports - I'll try to tackle this in the next week.
oh I'm sorry, mine is due to not having done `LIBCST_PARSER_TYPE=native`

Fix patch:
diff --git a/native/libcst/src/parser/grammar.rs b/native/libcst/src/parser/grammar.rs
index decf983bb..14622c1a9 100644
--- a/native/libcst/src/parser/grammar.rs
+++ b/native/libcst/src/parser/grammar.rs
@@ -516,7 +516,7 @@ parser! {
             }
 
         rule with_item() -> WithItem<'input, 'a>
-            = e:expression() a:lit("as") t:star_target() &(lit(",") / lit(":")) {
+            = e:expression() a:lit("as") t:star_target() &(lit(",") / lit(":") / rpar()) {
                 make_with_item(e, Some(a), Some(t))
             }
             / e:expression() {


Test patch:
diff --git a/native/libcst/tests/fixtures/with_wickedness.py b/native/libcst/tests/fixtures/with_wickedness.py
index 7cb5c67d3..ee6ff7b6f 100644
--- a/native/libcst/tests/fixtures/with_wickedness.py
+++ b/native/libcst/tests/fixtures/with_wickedness.py
@@ -1,13 +1,52 @@
 # with_wickedness
 
-with foo : ...
+with foo :
+    pass
+
+with foo, bar:
+    pass
+
+with (foo, bar):
+    pass
+
+with (foo, bar,):
+    pass
+
+with foo, bar as bar:
+    pass
+
+with (foo, bar as bar):
+    pass
+
+with (foo, bar as bar,):
+    pass
 
 async def f():
-    async with foo as bar:
+    async with foo:
 
         with bar:
             pass
 
-    async  with  foo(1+1)  as  bar  , 1  as (a, b, ) , 2 as [a, b] , 3 as a[b]  :
+    async with foo :
+        pass
+
+    async with foo, bar:
+        pass
+
+    async with (foo, bar):
         pass
 
+    async with (foo, bar,):
+        pass
+
+    async with foo, bar as bar:
+        pass
+
+    async with (foo, bar as bar):
+        pass
+
+    async with (foo, bar as bar,):
+        pass
+
+    async  with  foo(1+1)  as  bar  , 1  as (a, b, ) , 2 as [a, b] , 3 as a[b]  :
+        pass