Skip to content

Commit bec8d39

Browse files
authored
Add :migrate_atom_interpolations option to formatter (#15531)
1 parent 6f9f2d1 commit bec8d39

27 files changed

Lines changed: 105 additions & 66 deletions

lib/eex/lib/eex/engine.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ defmodule EEx.Engine do
195195
def handle_expr(state, "=", ast) do
196196
check_state!(state)
197197
%{binary: binary, dynamic: dynamic, vars_count: vars_count} = state
198-
var = Macro.var(:"arg#{vars_count}", __MODULE__)
198+
var = Macro.var(String.to_unsafe_atom("arg#{vars_count}"), __MODULE__)
199199

200200
ast =
201201
quote do

lib/elixir/lib/base.ex

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ defmodule Base do
436436
end
437437

438438
for {base, alphabet} <- [upper: b16_alphabet, lower: to_lower_enc.(b16_alphabet)] do
439-
name = :"encode16#{base}"
439+
name = String.to_unsafe_atom("encode16#{base}")
440440
encoded = to_encode_list.(alphabet)
441441

442442
@compile {:inline, [{name, 1}]}
@@ -617,10 +617,10 @@ defmodule Base do
617617
upper = Enum.with_index(b16_alphabet)
618618

619619
for {base, alphabet} <- [upper: upper, lower: to_lower_dec.(upper), mixed: to_mixed_dec.(upper)] do
620-
decode_name = :"decode16#{base}!"
621-
validate_name = :"validate16#{base}?"
622-
valid_char_name = :"valid_char16#{base}?"
623-
valid_word_name = :"valid_word16#{base}?"
620+
decode_name = String.to_unsafe_atom("decode16#{base}!")
621+
validate_name = String.to_unsafe_atom("validate16#{base}?")
622+
valid_char_name = String.to_unsafe_atom("valid_char16#{base}?")
623+
valid_word_name = String.to_unsafe_atom("valid_word16#{base}?")
624624

625625
{min, decoded} = to_decode_list.(alphabet)
626626

@@ -735,7 +735,7 @@ defmodule Base do
735735
end
736736

737737
for {base, alphabet} <- [base: b64_alphabet, url: b64url_alphabet] do
738-
name = :"encode64#{base}"
738+
name = String.to_unsafe_atom("encode64#{base}")
739739
encoded = to_encode_list.(alphabet)
740740

741741
@compile {:inline, [{name, 1}]}
@@ -1001,12 +1001,12 @@ defmodule Base do
10011001
end
10021002

10031003
for {base, alphabet} <- [base: b64_alphabet, url: b64url_alphabet] do
1004-
decode_name = :"decode64#{base}!"
1004+
decode_name = String.to_unsafe_atom("decode64#{base}!")
10051005

1006-
validate_name = :"validate64#{base}?"
1007-
validate_main_name = :"validate_main64#{validate_name}?"
1008-
valid_char_name = :"valid_char64#{base}?"
1009-
valid_word_name = :"valid_word64#{base}?"
1006+
validate_name = String.to_unsafe_atom("validate64#{base}?")
1007+
validate_main_name = String.to_unsafe_atom("validate_main64#{validate_name}?")
1008+
valid_char_name = String.to_unsafe_atom("valid_char64#{base}?")
1009+
valid_word_name = String.to_unsafe_atom("valid_word64#{base}?")
10101010
{min, decoded} = alphabet |> Enum.with_index() |> to_decode_list.()
10111011

10121012
# SWAR fast path: 7 bytes per stride, validated via `valid_word64<base>?`
@@ -1314,7 +1314,7 @@ defmodule Base do
13141314
hexupper: b32hex_alphabet,
13151315
hexlower: to_lower_enc.(b32hex_alphabet)
13161316
] do
1317-
name = :"encode32#{base}"
1317+
name = String.to_unsafe_atom("encode32#{base}")
13181318
encoded = to_encode_list.(alphabet)
13191319

13201320
@compile {:inline, [{name, 1}]}
@@ -1653,16 +1653,16 @@ defmodule Base do
16531653
hexlower: to_lower_dec.(hexupper),
16541654
hexmixed: to_mixed_dec.(hexupper)
16551655
] do
1656-
decode_name = :"decode32#{base}!"
1657-
validate_name = :"validate32#{base}?"
1658-
validate_main_name = :"validate_main32#{validate_name}?"
1659-
valid_char_name = :"valid_char32#{base}?"
1656+
decode_name = String.to_unsafe_atom("decode32#{base}!")
1657+
validate_name = String.to_unsafe_atom("validate32#{base}?")
1658+
validate_main_name = String.to_unsafe_atom("validate_main32#{validate_name}?")
1659+
valid_char_name = String.to_unsafe_atom("valid_char32#{base}?")
16601660
{min, decoded} = to_decode_list.(alphabet)
16611661

16621662
# SWAR fast path: 7 bytes per stride, validated via `valid_word32<base>?`
16631663
# in the body. Tail leftover (1-6 bytes after a 7-byte stride hits an
16641664
# 8-byte-multiple `main`) recurses through the single-byte clause.
1665-
valid_word_name = :"valid_word32#{base}?"
1665+
valid_word_name = String.to_unsafe_atom("valid_word32#{base}?")
16661666

16671667
defp unquote(validate_main_name)(<<w::56, rest::binary>>),
16681668
do: unquote(valid_word_name)(w) and unquote(validate_main_name)(rest)

lib/elixir/lib/code.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,12 @@ defmodule Code do
767767
* `:migrate` (since v1.18.0) - when `true`, sets all other migration options
768768
to `true` by default. Defaults to `false`.
769769
770+
* `:migrate_atom_interpolations` (since v1.21.0) - when `true`, rewrites
771+
deprecated atom interpolations to explicit calls to `String.to_unsafe_atom/1`.
772+
For example, `:"foo_#{bar}"` becomes `String.to_unsafe_atom("foo_#{bar}")`.
773+
Interpolated keywords like `["foo_#{bar}": 1]` are **not** migrated.
774+
Defaults to the value of the `:migrate` option. This option changes the AST.
775+
770776
* `:migrate_bitstring_modifiers` (since v1.18.0) - when `true`,
771777
removes unnecessary parentheses in known bitstring
772778
[modifiers](`<<>>/1`), for example `<<foo::binary()>>`

lib/elixir/lib/code/formatter.ex

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ defmodule Code.Formatter do
195195
file = Keyword.get(opts, :file, nil)
196196
sigils = Keyword.get(opts, :sigils, [])
197197
migrate = Keyword.get(opts, :migrate, false)
198+
migrate_atom_interpolations = Keyword.get(opts, :migrate_atom_interpolations, migrate)
198199
migrate_bitstring_modifiers = Keyword.get(opts, :migrate_bitstring_modifiers, migrate)
199200
migrate_call_parens_on_pipe = Keyword.get(opts, :migrate_call_parens_on_pipe, migrate)
200201
migrate_charlists_as_sigils = Keyword.get(opts, :migrate_charlists_as_sigils, migrate)
@@ -223,6 +224,7 @@ defmodule Code.Formatter do
223224
comments: comments,
224225
sigils: sigils,
225226
file: file,
227+
migrate_atom_interpolations: migrate_atom_interpolations,
226228
migrate_bitstring_modifiers: migrate_bitstring_modifiers,
227229
migrate_call_parens_on_pipe: migrate_call_parens_on_pipe,
228230
migrate_charlists_as_sigils: migrate_charlists_as_sigils,
@@ -334,14 +336,20 @@ defmodule Code.Formatter do
334336
end
335337

336338
defp quoted_to_algebra(
337-
{{:., _, [:erlang, :binary_to_atom]}, _, [{:<<>>, _, entries}, :utf8]} = quoted,
339+
{{:., _, [:erlang, :binary_to_atom]}, _, [{:<<>>, _, entries} = bitstring, :utf8]} =
340+
quoted,
338341
context,
339342
state
340343
) do
341-
if interpolated?(entries) do
342-
interpolation_to_algebra(entries, @double_quote, state, ":\"", @double_quote)
343-
else
344-
remote_to_algebra(quoted, context, state)
344+
cond do
345+
not interpolated?(entries) ->
346+
remote_to_algebra(quoted, context, state)
347+
348+
state.migrate_atom_interpolations ->
349+
quoted_to_algebra(quote(do: String.to_unsafe_atom(unquote(bitstring))), context, state)
350+
351+
true ->
352+
interpolation_to_algebra(entries, @double_quote, state, ":\"", @double_quote)
345353
end
346354
end
347355

lib/elixir/lib/io/ansi.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ defmodule IO.ANSI do
159159

160160
for font_n <- [1, 2, 3, 4, 5, 6, 7, 8, 9] do
161161
@doc "Sets alternative font #{font_n}."
162-
defsequence.(:"font_#{font_n}", font_n + 10, "m")
162+
defsequence.(String.to_unsafe_atom("font_#{font_n}"), font_n + 10, "m")
163163
end
164164

165165
@doc "Normal color or intensity."
@@ -193,13 +193,13 @@ defmodule IO.ANSI do
193193
defsequence.(color, code + 30, "m")
194194

195195
@doc "Sets foreground color to light #{color}."
196-
defsequence.(:"light_#{color}", code + 90, "m")
196+
defsequence.(String.to_unsafe_atom("light_#{color}"), code + 90, "m")
197197

198198
@doc "Sets background color to #{color}."
199-
defsequence.(:"#{color}_background", code + 40, "m")
199+
defsequence.(String.to_unsafe_atom("#{color}_background"), code + 40, "m")
200200

201201
@doc "Sets background color to light #{color}."
202-
defsequence.(:"light_#{color}_background", code + 100, "m")
202+
defsequence.(String.to_unsafe_atom("light_#{color}_background"), code + 100, "m")
203203
end
204204

205205
@doc "Default text color."

lib/elixir/lib/macro/env.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,13 @@ defmodule Macro.Env do
213213
@doc false
214214
@deprecated "Use Macro.Env.expand_alias/4 instead"
215215
def fetch_alias(%{__struct__: Macro.Env, aliases: aliases}, atom) when is_atom(atom),
216-
do: Keyword.fetch(aliases, :"Elixir.#{atom}")
216+
do: Keyword.fetch(aliases, String.to_unsafe_atom("Elixir.#{atom}"))
217217

218218
@doc false
219219
@deprecated "Use Macro.Env.expand_alias/4 instead"
220220
def fetch_macro_alias(%{__struct__: Macro.Env, macro_aliases: aliases}, atom)
221221
when is_atom(atom),
222-
do: Keyword.fetch(aliases, :"Elixir.#{atom}")
222+
do: Keyword.fetch(aliases, String.to_unsafe_atom("Elixir.#{atom}"))
223223

224224
@doc """
225225
Returns the modules from which the given `{name, arity}` was

lib/elixir/lib/module.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@ defmodule Module do
11921192
defp expand_key(key, counters) do
11931193
case counters do
11941194
%{^key => count} when is_integer(count) and count >= 1 ->
1195-
{{:"#{key}#{count}", [], Elixir}, Map.put(counters, key, count - 1)}
1195+
{{String.to_unsafe_atom("#{key}#{count}"), [], Elixir}, Map.put(counters, key, count - 1)}
11961196

11971197
_ ->
11981198
{{key, [], Elixir}, counters}
@@ -1225,7 +1225,8 @@ defmodule Module do
12251225
defp merge_signature({var, _, _} = older, {var, _, _}, _), do: older
12261226

12271227
# Otherwise, returns a generic guess
1228-
defp merge_signature({_, meta, _}, _newer, i), do: {:"arg#{i}", meta, Elixir}
1228+
defp merge_signature({_, meta, _}, _newer, i),
1229+
do: {String.to_unsafe_atom("arg#{i}"), meta, Elixir}
12291230

12301231
@doc """
12311232
Checks if the module defines the given function or macro.

lib/elixir/test/elixir/code_formatter/migration_test.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ defmodule Code.Formatter.MigrationTest do
1010

1111
@short_length [line_length: 10]
1212

13+
describe "migrate_atom_interpolations: true" do
14+
@opts [migrate_atom_interpolations: true]
15+
16+
test "replaces atom interpolations" do
17+
assert_format ~S/:"foo_#{i}"/, ~S/String.to_unsafe_atom("foo_#{i}")/, @opts
18+
end
19+
end
20+
1321
describe "migrate_bitstring_modifiers: true" do
1422
@opts [migrate_bitstring_modifiers: true]
1523

lib/elixir/test/elixir/file/stream_test.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ defmodule File.StreamTest do
2121
:erpc.call(node, File, :stream!, [src, modes, lines_or_bytes])
2222
end
2323

24-
distributed_node = :"secondary@#{node() |> Atom.to_string() |> :binary.split("@") |> tl()}"
24+
distributed_node =
25+
String.to_unsafe_atom("secondary@#{node() |> Atom.to_string() |> :binary.split("@") |> tl()}")
2526

2627
for {type, node} <- [local: node(), distributed: distributed_node] do
2728
describe "#{type} node" do

lib/elixir/test/elixir/module/types/descr_test.exs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ defmodule Module.Types.DescrTest do
3333
opt_difference(
3434
acc,
3535
open_map([
36-
{:k, open_map([{:"value#{index}", integer()}])},
37-
{:"field#{index}", integer()}
36+
{:k, open_map([{String.to_unsafe_atom("value#{index}"), integer()}])},
37+
{String.to_unsafe_atom("field#{index}"), integer()}
3838
])
3939
)
4040
end)
@@ -45,7 +45,7 @@ defmodule Module.Types.DescrTest do
4545
opt_difference(
4646
acc,
4747
open_tuple([
48-
open_tuple([atom([:"value#{index}"])]),
48+
open_tuple([atom([String.to_unsafe_atom("value#{index}")])]),
4949
integer()
5050
])
5151
)
@@ -3954,7 +3954,10 @@ defmodule Module.Types.DescrTest do
39543954
tuple([atom([:font_style]), atom([:italic])]),
39553955
Enum.reduce(
39563956
for elem1 <- 1..5, elem2 <- 1..5 do
3957-
tuple([atom([:"f#{elem1}"]), atom([:"s#{elem2}"])])
3957+
tuple([
3958+
atom([String.to_unsafe_atom("f#{elem1}")]),
3959+
atom([String.to_unsafe_atom("s#{elem2}")])
3960+
])
39583961
end,
39593962
&opt_union/2
39603963
)
@@ -4008,7 +4011,7 @@ defmodule Module.Types.DescrTest do
40084011
{:notifications, boolean()}
40094012
] ++
40104013
Enum.map(1..50, fn i ->
4011-
{:"field_#{i}", atom([:"value_#{i}"])}
4014+
{String.to_unsafe_atom("field_#{i}"), atom([String.to_unsafe_atom("value_#{i}")])}
40124015
end)
40134016
)
40144017

@@ -4021,7 +4024,7 @@ defmodule Module.Types.DescrTest do
40214024

40224025
expected =
40234026
for i <- 1..50 do
4024-
name = :"name_#{i}"
4027+
name = String.to_unsafe_atom("name_#{i}")
40254028
closed_map([__struct__: atom([name])] ++ [{name, binary()}])
40264029
end
40274030
|> Enum.reduce(&opt_union/2)

0 commit comments

Comments
 (0)