From 70d759847d09bc907308ee4a6ec2b82e633cfb50 Mon Sep 17 00:00:00 2001 From: steve Date: Tue, 1 Jul 2025 15:04:33 -0700 Subject: [PATCH 1/4] add failing test --- .../metadata/tests/test_position_provider.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/libcst/metadata/tests/test_position_provider.py b/libcst/metadata/tests/test_position_provider.py index c479837e2..50e9b539b 100644 --- a/libcst/metadata/tests/test_position_provider.py +++ b/libcst/metadata/tests/test_position_provider.py @@ -166,3 +166,25 @@ def test_position(self) -> None: # check syntactic position ignores whitespace self.assertEqual(state.provider._computed[node], CodeRange((1, 1), (1, 5))) + + +class MatchProviderTest(UnitTest): + def test_match_provider(self) -> None: + class Visitor(cst.CSTVisitor): + METADATA_DEPENDENCIES = (PositionProvider,) + + def on_visit(self, node): + print(self.get_metadata(PositionProvider, node).start.line) + return super().on_visit(node) + + + code = """ +match status: + case b: pass + case c: pass + case _: pass +""" + + wrapper = MetadataWrapper(parse_module(code)) + visitor = Visitor() + module = wrapper.visit(visitor) \ No newline at end of file From 3326f91ba132d000d88830381e72d3f418c91833 Mon Sep 17 00:00:00 2001 From: steve Date: Tue, 1 Jul 2025 15:39:13 -0700 Subject: [PATCH 2/4] fix issue --- libcst/_nodes/statement.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index 1aba38d30..cdc49edc5 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -2886,6 +2886,9 @@ def _codegen_impl(self, state: CodegenState) -> None: state.add_token("if") self.whitespace_after_if._codegen(state) guard._codegen(state) + else: + self.whitespace_before_if._codegen(state) + self.whitespace_after_if._codegen(state) self.whitespace_before_colon._codegen(state) state.add_token(":") @@ -3473,6 +3476,13 @@ def _codegen_impl(self, state: CodegenState) -> None: state.add_token(" ") elif isinstance(ws_after, BaseParenthesizableWhitespace): ws_after._codegen(state) + else: + ws_before = self.whitespace_before_as + if isinstance(ws_before, BaseParenthesizableWhitespace): + ws_before._codegen(state) + ws_after = self.whitespace_after_as + if isinstance(ws_after, BaseParenthesizableWhitespace): + ws_after._codegen(state) if name is None: state.add_token("_") else: From 011096da0193b5e1254b1149751e513fde7bc002 Mon Sep 17 00:00:00 2001 From: steve Date: Tue, 1 Jul 2025 15:52:54 -0700 Subject: [PATCH 3/4] fixes an issue with PositionProvider not working with case statement --- .../metadata/tests/test_position_provider.py | 72 +++++++++++++------ 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/libcst/metadata/tests/test_position_provider.py b/libcst/metadata/tests/test_position_provider.py index 50e9b539b..ea3ec9c22 100644 --- a/libcst/metadata/tests/test_position_provider.py +++ b/libcst/metadata/tests/test_position_provider.py @@ -83,6 +83,56 @@ def visit_Pass(self, node: cst.Pass) -> None: wrapper = MetadataWrapper(parse_module("pass")) wrapper.visit_batched([ABatchable()]) + def test_match_statement_position_metadata(self) -> None: + test = self + + class MatchPositionVisitor(CSTVisitor): + METADATA_DEPENDENCIES = (PositionProvider,) + + def visit_Match(self, node: cst.Match) -> None: + # Test that match statement has correct position + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((2, 0), (5, 16)), + ) + + def visit_MatchCase(self, node: cst.MatchCase) -> None: + # Test that match cases have correct positions + if ( + isinstance(node.pattern, cst.MatchAs) + and node.pattern.name + and node.pattern.name.value == "b" + ): + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((3, 4), (3, 16)), + ) + elif ( + isinstance(node.pattern, cst.MatchAs) + and node.pattern.name + and node.pattern.name.value == "c" + ): + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((4, 4), (4, 16)), + ) + elif isinstance(node.pattern, cst.MatchAs) and not node.pattern.name: + # This is the wildcard case `case _: pass` + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((5, 4), (5, 16)), + ) + + code = """ +match status: + case b: pass + case c: pass + case _: pass +""" + + wrapper = MetadataWrapper(parse_module(code)) + wrapper.visit(MatchPositionVisitor()) + class PositionProvidingCodegenStateTest(UnitTest): def test_codegen_initial_position(self) -> None: @@ -166,25 +216,3 @@ def test_position(self) -> None: # check syntactic position ignores whitespace self.assertEqual(state.provider._computed[node], CodeRange((1, 1), (1, 5))) - - -class MatchProviderTest(UnitTest): - def test_match_provider(self) -> None: - class Visitor(cst.CSTVisitor): - METADATA_DEPENDENCIES = (PositionProvider,) - - def on_visit(self, node): - print(self.get_metadata(PositionProvider, node).start.line) - return super().on_visit(node) - - - code = """ -match status: - case b: pass - case c: pass - case _: pass -""" - - wrapper = MetadataWrapper(parse_module(code)) - visitor = Visitor() - module = wrapper.visit(visitor) \ No newline at end of file From 38f22c625eed75ef9b2032367980097e71c93761 Mon Sep 17 00:00:00 2001 From: steve Date: Tue, 1 Jul 2025 16:32:36 -0700 Subject: [PATCH 4/4] remove comments --- libcst/metadata/tests/test_position_provider.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/libcst/metadata/tests/test_position_provider.py b/libcst/metadata/tests/test_position_provider.py index ea3ec9c22..14cecec70 100644 --- a/libcst/metadata/tests/test_position_provider.py +++ b/libcst/metadata/tests/test_position_provider.py @@ -90,14 +90,12 @@ class MatchPositionVisitor(CSTVisitor): METADATA_DEPENDENCIES = (PositionProvider,) def visit_Match(self, node: cst.Match) -> None: - # Test that match statement has correct position test.assertEqual( self.get_metadata(PositionProvider, node), CodeRange((2, 0), (5, 16)), ) def visit_MatchCase(self, node: cst.MatchCase) -> None: - # Test that match cases have correct positions if ( isinstance(node.pattern, cst.MatchAs) and node.pattern.name @@ -117,7 +115,6 @@ def visit_MatchCase(self, node: cst.MatchCase) -> None: CodeRange((4, 4), (4, 16)), ) elif isinstance(node.pattern, cst.MatchAs) and not node.pattern.name: - # This is the wildcard case `case _: pass` test.assertEqual( self.get_metadata(PositionProvider, node), CodeRange((5, 4), (5, 16)),