Repository: Instagram/LibCST (language: python)

Issue:
Empty brackets around the `case` of a `match-case` are incorrectly parsed as a pair of square brackets
This is an oddity which results in data not roundtripping through libcst accurately. It also makes it impossible to know what the original file contents were.

If a match-case is used and the case is an unparenthesized, unbracketed expression, libcst parses this to have `lbracket` and `rbracket` incorrectly populated.

Here's a reproducer:
```python
x = (1, 2)
match x:
    case 1, 2:
        print("x matches (1, 2)")
    case _:
        print("x does not match (1, 2)")
```

Attempting to simply load and dump this module produces `case [1, 2]:`.

Inspecting the results of `parse_module`, I find a parse shaped like...
```
pattern=MatchList(...,
  lbracket=LeftSquareBracket(
    whitespace_after=SimpleWhitespace(
      value='',
    ),
  ),
  rbracket=RightSquareBracket(
    whitespace_before=SimpleWhitespace(
      value='',
    ),
  ),
  lpar=[],
  rpar=[],
)
```


Discussion / hints:
Whoa this is pretty bad

Fix patch:
diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py
index a9502da8f..bf9e8ecc1 100644
--- a/libcst/_nodes/statement.py
+++ b/libcst/_nodes/statement.py
@@ -3071,10 +3071,10 @@ class MatchList(MatchSequence):
     patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
 
     #: An optional left bracket. If missing, this is an open sequence pattern.
-    lbracket: Optional[LeftSquareBracket] = LeftSquareBracket.field()
+    lbracket: Optional[LeftSquareBracket] = None
 
     #: An optional left bracket. If missing, this is an open sequence pattern.
-    rbracket: Optional[RightSquareBracket] = RightSquareBracket.field()
+    rbracket: Optional[RightSquareBracket] = None
 
     #: Parenthesis at the beginning of the node
     lpar: Sequence[LeftParen] = ()


Test patch:
diff --git a/libcst/tests/test_roundtrip.py b/libcst/tests/test_roundtrip.py
new file mode 100644
index 000000000..010d840d0
--- /dev/null
+++ b/libcst/tests/test_roundtrip.py
@@ -0,0 +1,26 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+from pathlib import Path
+from unittest import TestCase
+
+from libcst import parse_module
+from libcst._parser.entrypoints import is_native
+
+fixtures: Path = Path(__file__).parent.parent.parent / "native/libcst/tests/fixtures"
+
+
+class RoundTripTests(TestCase):
+    def test_clean_roundtrip(self) -> None:
+        if not is_native():
+            self.skipTest("pure python parser doesn't work with this")
+        self.assertTrue(fixtures.exists(), f"{fixtures} should exist")
+        files = list(fixtures.iterdir())
+        self.assertGreater(len(files), 0)
+        for file in files:
+            with self.subTest(file=str(file)):
+                src = file.read_text(encoding="utf-8")
+                mod = parse_module(src)
+                self.assertEqual(mod.code, src)
diff --git a/native/libcst/tests/fixtures/malicious_match.py b/native/libcst/tests/fixtures/malicious_match.py
index cabf46890..8c46571f9 100644
--- a/native/libcst/tests/fixtures/malicious_match.py
+++ b/native/libcst/tests/fixtures/malicious_match.py
@@ -36,4 +36,5 @@
     case Foo  |    Bar |  (  Baz):  pass
     case x,y  ,  * more   :pass
     case y.z: pass
+    case 1, 2: pass