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
import libcst.matchers as m
import libcst
module = libcst.parse_module('x * y')
matcher = m.Name(value='x')
new_module = m.replace(module, matcher, libcst.parse_expression("1 + 2"))
print(new_module.code)
will output
whereas the expected code is:
I think it's reasonable to expect that a module is equivalent to parse_module(module.code).