-
Notifications
You must be signed in to change notification settings - Fork 192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Generated Code not equivalent to AST #341
Comments
Good catch! The parentheses are added by attributes |
@jimmylai I'm not sure Maybe an option would be to change Then @contextmanager
def _parenthesize(self, parent: CSTNode, state: CodegenState) -> Generator[None, None, None]:
need_pars = need_parentheses(self, parent) # new code
if need_pars: # new code
LeftParen()._codegen(state) # new code
for lpar in self.lpar:
lpar._codegen(state)
with state.record_syntactic_position(self):
yield
for rpar in self.rpar:
rpar._codegen(state)
if need_pars: # new code
RightParen()._codegen(state) # new code Note that this does not only affect
I do have a custom implementation of |
In our design, every LibCST node is an immutable dataclass instance and thus we only provide transformer patter for modifying the tree. E.g. https://github.com/Instagram/LibCST/blob/master/libcst/_nodes/module.py#L33-L35 The matcher Do you still think adding the logic in |
Hi @jimmylai, I still think that For example, the code below also triggers the bug: import libcst
module = libcst.Module(body=[
libcst.BinaryOperation(
left = libcst.parse_expression("1 + 2"),
operator = libcst.Multiply(),
right = libcst.Integer("3")
)
])
print(module.code) producing the output In this case |
@jimmylai WDYT? |
Yeah, the example tree you provided is created from bottom up and it's possible to create a tree like that. LibCST/libcst/_nodes/module.py Line 128 in c023fa7
|
I'm not suggesting to use the metadata in codegen, just to change With your proposal you will be traversing the tree twice, once to fix the parentheses and a second time to generate the code. |
@jimmylai Any chance to revisit this? It'd be great if we could discuss and agree on a proposal to fix this. After looking deeper into how other syntactic elements are treated, I think we should also discuss whether Also related to how some other elements are generated, another alternative is to check in the parent whether a child needs parentheses. |
I just ran into this bug as well. It may not be pretty but a simple work around is: class FixParens(cst.CSTTransformer):
def _wrap_with_parens(self, node):
return node.with_changes(
lpar=[cst.LeftParen()],
rpar=[cst.RightParen()],
)
def leave_BinaryOperation(self,
original_node: cst.BinaryOperation,
update_node: cst.BinaryOperation
) -> cst.BinaryOperation:
if not update_node.lpar:
assert not update_node.rpar
return self._wrap_with_parens(update_node)
return update_node
# similar for leave_UnaryOperation Now this has the downside that it inserts parens where they are not needed but does guarantee hand built expressions are properly grouped. |
That solution would make it impracticable to use in a code refactoring tool, as lots of unneeded parentheses would be added and unfortunately code formatters do not remove extra parens. I do have a more comprehensive transformer that only adds the parenthesis when needed but the problem is that it requires the parent and |
@sk- agreed. I actually use an additional guard ( |
When doing replacements in a node, it's easy to end up with a tree, which won't get correctly converted to source code. The issue is that
libcst
is not adding the necessary parentheses.For example, the code below
will output
whereas the expected code is:
I think it's reasonable to expect that a
module
is equivalent toparse_module(module.code)
.The text was updated successfully, but these errors were encountered: