Skip to content

Commit 1f14211

Browse files
committed
Handle false positive on List.to_charlist/1 formatting, closes #12248
1 parent 1ae36fa commit 1f14211

File tree

2 files changed

+43
-25
lines changed

2 files changed

+43
-25
lines changed

lib/elixir/lib/code/normalizer.ex

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ defmodule Code.Normalizer do
7070
# Atoms with interpolations
7171
defp do_normalize(
7272
{{:., dot_meta, [:erlang, :binary_to_atom]}, call_meta,
73-
[{:<<>>, _, args} = string, :utf8]},
73+
[{:<<>>, _, parts} = string, :utf8]},
7474
state
7575
)
76-
when is_list(args) do
76+
when is_list(parts) do
7777
dot_meta = patch_meta_line(dot_meta, state.parent_meta)
7878
call_meta = patch_meta_line(call_meta, dot_meta)
7979

8080
utf8 =
81-
if args == [] or interpolated?(string) do
81+
if parts == [] or binary_interpolated?(parts) do
8282
# a non-normalized :utf8 atom signals an atom interpolation
8383
:utf8
8484
else
@@ -96,23 +96,27 @@ defmodule Code.Normalizer do
9696
end
9797

9898
# Charlists with interpolations
99-
defp do_normalize({{:., dot_meta, [List, :to_charlist]}, call_meta, [parts]}, state) do
100-
parts =
101-
Enum.map(parts, fn
102-
{{:., part_dot_meta, [Kernel, :to_string]}, part_call_meta, args} ->
103-
args = normalize_args(args, state)
104-
105-
{{:., part_dot_meta, [Kernel, :to_string]}, part_call_meta, args}
106-
107-
part ->
108-
if state.escape do
109-
maybe_escape_literal(part, state)
110-
else
111-
part
112-
end
113-
end)
99+
defp do_normalize({{:., dot_meta, [List, :to_charlist]}, call_meta, [parts]} = quoted, state) do
100+
if list_interpolated?(parts) do
101+
parts =
102+
Enum.map(parts, fn
103+
{{:., part_dot_meta, [Kernel, :to_string]}, part_call_meta, args} ->
104+
args = normalize_args(args, state)
105+
106+
{{:., part_dot_meta, [Kernel, :to_string]}, part_call_meta, args}
107+
108+
part when is_binary(part) ->
109+
if state.escape do
110+
maybe_escape_literal(part, state)
111+
else
112+
part
113+
end
114+
end)
114115

115-
{{:., dot_meta, [List, :to_charlist]}, call_meta, [parts]}
116+
{{:., dot_meta, [List, :to_charlist]}, call_meta, [parts]}
117+
else
118+
normalize_call(quoted, state)
119+
end
116120
end
117121

118122
# Don't normalize the `Access` atom in access syntax
@@ -389,11 +393,11 @@ defmodule Code.Normalizer do
389393
defp allow_keyword?(:{}, _), do: false
390394
defp allow_keyword?(op, arity), do: not is_atom(op) or not Macro.operator?(op, arity)
391395

392-
defp normalize_bitstring({:<<>>, meta, parts} = quoted, state, escape_interpolation \\ false) do
396+
defp normalize_bitstring({:<<>>, meta, parts}, state, escape_interpolation \\ false) do
393397
meta = patch_meta_line(meta, state.parent_meta)
394398

395399
parts =
396-
if interpolated?(quoted) do
400+
if binary_interpolated?(parts) do
397401
normalize_interpolation_parts(parts, %{state | parent_meta: meta}, escape_interpolation)
398402
else
399403
state = %{state | parent_meta: meta}
@@ -543,17 +547,20 @@ defmodule Code.Normalizer do
543547
term
544548
end
545549

546-
# Check if we have an interpolated string.
547-
defp interpolated?({:<<>>, _, [_ | _] = parts}) do
550+
defp binary_interpolated?(parts) do
548551
Enum.all?(parts, fn
549552
{:"::", _, [{{:., _, [Kernel, :to_string]}, _, [_]}, {:binary, _, _}]} -> true
550553
binary when is_binary(binary) -> true
551554
_ -> false
552555
end)
553556
end
554557

555-
defp interpolated?(_) do
556-
false
558+
defp list_interpolated?(parts) do
559+
Enum.all?(parts, fn
560+
{{:., _, [Kernel, :to_string]}, _, [_]} -> true
561+
binary when is_binary(binary) -> true
562+
_ -> false
563+
end)
557564
end
558565

559566
defp patch_meta_line(meta, parent_meta) do

lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,17 @@ defmodule Code.Normalizer.QuotedASTTest do
515515
test "charlist" do
516516
assert quoted_to_string(quote(do: [])) == "[]"
517517
assert quoted_to_string(quote(do: 'abc')) == "'abc'"
518+
519+
# False positive
520+
assert quoted_to_string(
521+
quote do
522+
:"Elixir.List".to_charlist([
523+
case var do
524+
var -> var
525+
end
526+
])
527+
end
528+
) =~ "List.to_charlist([\n case var do\n var -> var\n end\n])"
518529
end
519530

520531
test "string" do

0 commit comments

Comments
 (0)