Repository: Instagram/LibCST

Issue:
CST node validation error for match without whitespace
Short reproducer:
```
match(x):
    case(y):
        pass
```

Crash:
```
thread '<unnamed>' panicked at 'conversion failed for Match: PyErr { type: <class 'libcst._nodes.base.CSTValidationError'>, value: CSTValidationError("Must have at least one space after a 'match' keyword"), traceback: Some(<traceback object at 0x7f38d11c7fc0>) }', libcst/src/nodes/statement.rs:2185:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Traceback (most recent call last):
  File "/usr/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/isidentical/projects/LibCST/libcst/tool.py", line 839, in <module>
    main(os.environ.get("LIBCST_TOOL_COMMAND_NAME", "libcst.tool"), sys.argv[1:])
  File "/home/isidentical/projects/LibCST/libcst/tool.py", line 834, in main
    return lookup.get(args.action or None, _invalid_command)(proc_name, command_args)
  File "/home/isidentical/projects/LibCST/libcst/tool.py", line 278, in _print_tree_impl
    tree = parse_module(
  File "/home/isidentical/projects/LibCST/libcst/_parser/entrypoints.py", line 109, in parse_module
    result = _parse(
  File "/home/isidentical/projects/LibCST/libcst/_parser/entrypoints.py", line 55, in _parse
    return parse(source_str)
pyo3_runtime.PanicException: conversion failed for Match: PyErr { type: <class 'libcst._nodes.base.CSTValidationError'>, value: CSTValidationError("Must have at least one space after a 'match' keyword"), traceback: Some(<traceback object at 0x7f38d11c7fc0>) }
```

Comments:

Patch:
diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py
index ded7c7c..0e1cefe 100644
--- a/libcst/_nodes/statement.py
+++ b/libcst/_nodes/statement.py
@@ -2687,11 +2687,6 @@ class Match(BaseCompoundStatement):
         if len(self.cases) == 0:
             raise CSTValidationError("A match statement must have at least one case.")
 
-        if self.whitespace_after_match.empty:
-            raise CSTValidationError(
-                "Must have at least one space after a 'match' keyword"
-            )
-
         indent = self.indent
         if indent is not None:
             if len(indent) == 0:


Test patch:
diff --git a/libcst/_nodes/tests/test_match.py b/libcst/_nodes/tests/test_match.py
index edf51d8..5ceea72 100644
--- a/libcst/_nodes/tests/test_match.py
+++ b/libcst/_nodes/tests/test_match.py
@@ -425,6 +425,34 @@ class MatchTest(CSTNodeTest):
                 + "    case None | False | True: pass\n",
                 "parser": None,
             },
+            # Match without whitespace between keyword and the expr
+            {
+                "node": cst.Match(
+                    subject=cst.Name(
+                        "x", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
+                    ),
+                    cases=[
+                        cst.MatchCase(
+                            pattern=cst.MatchSingleton(
+                                cst.Name(
+                                    "None",
+                                    lpar=[cst.LeftParen()],
+                                    rpar=[cst.RightParen()],
+                                )
+                            ),
+                            body=cst.SimpleStatementSuite((cst.Pass(),)),
+                            whitespace_after_case=cst.SimpleWhitespace(
+                                value="",
+                            ),
+                        ),
+                    ],
+                    whitespace_after_match=cst.SimpleWhitespace(
+                        value="",
+                    ),
+                ),
+                "code": "match(x):\n    case(None): pass\n",
+                "parser": parser,
+            },
         )
     )
     def test_valid(self, **kwargs: Any) -> None: