Repository: ikamensh/flynt

Issue:
transform-joins breaks code
Here's my flynt output:
```
Running flynt v.1.0.0
Using config file at /asdf/qwer/pyproject.toml
Using following options: Namespace(verbose=True, quiet=False, no_multiline=False, line_length=88, dry_run=True, stdout=False, string=False, transform_percent=True, transform_format=True, transform_concats=True, transform_joins=True, fail_on_change=False, aggressive=False, exclude=None, src=['.dev/bug.py'], version=False)
Running flynt in dry-run mode. No files will be changed.
--- /asdf/qwer/bug.py

+++

@@ -1,2 +1,2 @@

-';'.join(['a', 'b', 'c'])
+";\"\"\".join(['a', 'b', 'c"

fstringifying /asdf/qwer/bug.py...modified

Flynt run has finished. Stats:

Execution time:                            0.003s
Files checked:                             1
Files modified:                            1
Character count reduction:                 25 (96.15%)

Per expression type:
No old style (`%`) expressions attempted.
No `.format(...)` calls attempted.
No concatenations attempted.
Static string joins attempted:             1/1 (100.0%)
F-string expressions created:              1

_-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_._-_.
Please run your tests before committing. Did flynt get a perfect conversion? give it a star at:
~ https://github.com/ikamensh/flynt ~
Thank you for using flynt. Upgrade more projects and recommend it to your colleagues!
```
Python version is 3.11.3

Discussion:

Patch:
diff --git a/pyproject.toml b/pyproject.toml
index 22220a9..9bbd0af 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -83,7 +83,7 @@ max-branches = 18
 max-statements = 67
 
 [tool.ruff.per-file-ignores]
-"test/*" = ["I"]
+"test/*" = ["I", "S"]
 "test/integration/*" = [
     "F523",
     "F821",
diff --git a/src/flynt/__init__.py b/src/flynt/__init__.py
index 5f94a49..0019830 100644
--- a/src/flynt/__init__.py
+++ b/src/flynt/__init__.py
@@ -2,7 +2,7 @@
 from old "%-formatted" and .format(...) strings into Python 3.6+'s f-strings.
 Learn more about f-strings at https://www.python.org/dev/peps/pep-0498/"""
 
-__version__ = "1.0.0"
+__version__ = "1.0.1"
 
 from flynt.cli import main
 
diff --git a/src/flynt/static_join/transformer.py b/src/flynt/static_join/transformer.py
index 7314b1a..8e86896 100644
--- a/src/flynt/static_join/transformer.py
+++ b/src/flynt/static_join/transformer.py
@@ -42,6 +42,10 @@ class JoinTransformer(ast.NodeTransformer):
 def transform_join(tree: ast.AST, *args, **kwargs) -> Tuple[str, bool]:
 
     jt = JoinTransformer()
-    jt.visit(tree)
-    new_code = fixup_transformed(tree)
-    return new_code, jt.counter > 0
+    new_tree = jt.visit(tree)
+    changed = jt.counter > 0
+    if changed:
+        new_code = fixup_transformed(new_tree)
+    else:
+        new_code = ""
+    return new_code, changed
diff --git a/src/flynt/string_concat/transformer.py b/src/flynt/string_concat/transformer.py
index 31fadb3..8276c3c 100644
--- a/src/flynt/string_concat/transformer.py
+++ b/src/flynt/string_concat/transformer.py
@@ -65,6 +65,9 @@ def transform_concat(tree: ast.AST, *args, **kwargs) -> Tuple[str, bool]:
 
     ft = ConcatTransformer()
     new = ft.visit(tree)
-    new_code = fixup_transformed(new)
-
-    return new_code, ft.counter > 0
+    changed = ft.counter > 0
+    if changed:
+        new_code = fixup_transformed(new)
+    else:
+        new_code = ""
+    return new_code, changed
diff --git a/src/flynt/utils/utils.py b/src/flynt/utils/utils.py
index 7db23ef..a97b21b 100644
--- a/src/flynt/utils/utils.py
+++ b/src/flynt/utils/utils.py
@@ -101,7 +101,18 @@ def ast_string_node(string: str) -> ast.Str:
     return ast.Str(s=string)
 
 
+def check_is_string_node(tree: ast.AST):
+    """Raise an exception is tree doesn't represent a string"""
+    if isinstance(tree, ast.Module):
+        tree = tree.body[0]
+    if isinstance(tree, ast.Expr):
+        tree = tree.value
+    assert isinstance(tree, (ast.JoinedStr, ast.Str)), f"found {type(tree)}"
+
+
 def fixup_transformed(tree: ast.AST, quote_type: Optional[str] = None) -> str:
+    """Given a transformed string / fstring ast node, transform it to a string."""
+    # check_is_string_node(tree)
     il = FstrInliner()
     il.visit(tree)
     new_code = ast_to_string(tree)
@@ -112,6 +123,7 @@ def fixup_transformed(tree: ast.AST, quote_type: Optional[str] = None) -> str:
         new_code = set_quote_type(new_code, quote_type)
     new_code = new_code.replace("\n", "\\n")
     new_code = new_code.replace("\t", "\\t")
+    # ast.parse(new_code)
     return new_code
 
 


Test patch:
diff --git a/test/test_edits.py b/test/test_edits.py
index 93e327f..cefda7d 100644
--- a/test/test_edits.py
+++ b/test/test_edits.py
@@ -663,3 +663,12 @@ def test_literal_direct(state: State):
     out, count = code_editor.fstringify_code_by_line(s_in, state)
     assert count == 1
     assert out == expected_out
+
+
+def test_joins():
+
+    s_in = """';'.join(['a', 'b', 'c'])"""
+    expected_out = '"a;b;c"'
+    out, count = code_editor.fstringify_static_joins(s_in, State())
+    assert count > 0
+    assert out == expected_out