Skip to content

Commit 1e33413

Browse files
authored
Fix Macro.to_string/1 and ExUnit diffs for multi-letter sigils (#12627)
1 parent aef0879 commit 1e33413

File tree

5 files changed

+85
-7
lines changed

5 files changed

+85
-7
lines changed

lib/elixir/lib/code/normalizer.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ defmodule Code.Normalizer do
183183
defp do_normalize({sigil, meta, [{:<<>>, _, args} = string, modifiers]} = quoted, state)
184184
when is_list(args) and is_atom(sigil) do
185185
case Atom.to_string(sigil) do
186-
<<"sigil_", _name>> ->
186+
"sigil_" <> _ ->
187187
meta =
188188
meta
189189
|> patch_meta_line(state.parent_meta)

lib/elixir/lib/macro.ex

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,11 +1560,18 @@ defmodule Macro do
15601560
{left, right} = delimiter_pair(delimiter)
15611561

15621562
case Atom.to_string(sigil) do
1563-
<<"sigil_", name>> when name >= ?A and name <= ?Z ->
1564-
args = sigil_args(args, fun)
1565-
{:<<>>, _, [binary]} = parts
1566-
formatted = <<?~, name, left::binary, binary::binary, right::binary, args::binary>>
1567-
{:ok, fun.(ast, formatted)}
1563+
<<"sigil_", first, rest::binary>> when first >= ?A and first <= ?Z ->
1564+
if upcase_letters?(rest) do
1565+
args = sigil_args(args, fun)
1566+
{:<<>>, _, [binary]} = parts
1567+
1568+
formatted =
1569+
<<?~, first, rest::binary, left::binary, binary::binary, right::binary, args::binary>>
1570+
1571+
{:ok, fun.(ast, formatted)}
1572+
else
1573+
:error
1574+
end
15681575

15691576
<<"sigil_", name>> when name >= ?a and name <= ?z ->
15701577
args = sigil_args(args, fun)
@@ -1580,6 +1587,15 @@ defmodule Macro do
15801587
:error
15811588
end
15821589

1590+
defp upcase_letters?(<<letter, rest::binary>>) when letter >= ?A and letter <= ?Z,
1591+
do: upcase_letters?(rest)
1592+
1593+
defp upcase_letters?(<<_>>),
1594+
do: false
1595+
1596+
defp upcase_letters?(<<>>),
1597+
do: true
1598+
15831599
defp delimiter_pair("["), do: {"[", "]"}
15841600
defp delimiter_pair("{"), do: {"{", "}"}
15851601
defp delimiter_pair("("), do: {"(", ")"}

lib/elixir/src/elixir_tokenizer.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ tokenize_sigil([$~ | T], Line, Column, Scope, Tokens) ->
15361536
case tokenize_sigil_name(T, [], Line, Column + 1, Scope, Tokens) of
15371537
{ok, Name, Rest, NewLine, NewColumn, NewScope, NewTokens} ->
15381538
tokenize_sigil_contents(Rest, Name, NewLine, NewColumn, NewScope, NewTokens);
1539-
1539+
15401540
{error, Message, Token} ->
15411541
Reason = {Line, Column, Message, Token},
15421542
error(Reason, T, Scope, Tokens)

lib/elixir/test/elixir/macro_test.exs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,32 @@ defmodule MacroTest do
569569
assert Macro.to_string(576_460_752_303_423_455) == "576_460_752_303_423_455"
570570
assert Macro.to_string(-576_460_752_303_423_455) == "-576_460_752_303_423_455"
571571
end
572+
573+
defmodule HTML do
574+
defstruct [:string]
575+
576+
defimpl Inspect do
577+
def inspect(%{string: string}, _) do
578+
"~HTML[#{string}]"
579+
end
580+
end
581+
end
582+
583+
defmacro sigil_HTML({:<<>>, _, [string]}, []) do
584+
Macro.escape(%HTML{string: string})
585+
end
586+
587+
test "sigils" do
588+
assert Macro.to_string(quote(do: ~HTML[hi])) == ~S/~HTML[hi]/
589+
590+
assert Macro.to_string(
591+
quote do
592+
~HTML"""
593+
hi
594+
"""
595+
end
596+
) == ~s[~HTML"""\nhi\n"""]
597+
end
572598
end
573599

574600
describe "to_string/2" do
@@ -660,6 +686,8 @@ defmodule MacroTest do
660686
assert macro_to_string(quote(do: ~S["'(123)'"])) == ~S/~S["'(123)'"]/
661687
assert macro_to_string(quote(do: ~s"#{"foo"}")) == ~S/~s"#{"foo"}"/
662688

689+
assert macro_to_string(quote(do: ~HTML[hi])) == ~S/~HTML[hi]/
690+
663691
assert macro_to_string(
664692
quote do
665693
~s"""
@@ -699,6 +727,14 @@ defmodule MacroTest do
699727
"""
700728
end
701729
) == ~s[~S"""\n"123"\n"""]
730+
731+
assert macro_to_string(
732+
quote do
733+
~HTML"""
734+
"123"
735+
"""
736+
end
737+
) == ~s[~HTML"""\n"123"\n"""]
702738
end
703739

704740
test "tuple call" do

lib/ex_unit/test/ex_unit/diff_test.exs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ defmodule ExUnit.DiffTest do
2626
end
2727
end
2828

29+
defmodule HTML do
30+
defstruct [:string]
31+
32+
defimpl Inspect do
33+
def inspect(%{string: string}, _) do
34+
"~HTML[#{string}]"
35+
end
36+
end
37+
end
38+
39+
defmacro sigil_HTML({:<<>>, _, [string]}, []) do
40+
Macro.escape(%HTML{string: string})
41+
end
42+
2943
defmacrop one, do: 1
3044

3145
defmacrop tuple(a, b) do
@@ -642,6 +656,12 @@ defmodule ExUnit.DiffTest do
642656
~s/-~D[2017-10-02]-/,
643657
~s/+"2017-10-01"+/
644658
)
659+
660+
refute_diff(
661+
~HTML[hi] = ~HTML[bye],
662+
"-~HTML[hi]-",
663+
"~HTML[+bye+]"
664+
)
645665
end
646666

647667
test "structs with missing keys on match" do
@@ -682,6 +702,12 @@ defmodule ExUnit.DiffTest do
682702
~s/-~D[2017-10-02]-/,
683703
~s/+"2017-10-01"+/
684704
)
705+
706+
refute_diff(
707+
~HTML[hi] == ~HTML[bye],
708+
"~HTML[-hi-]",
709+
"~HTML[+bye+]"
710+
)
685711
end
686712

687713
test "structs with same inspect but different inside match" do

0 commit comments

Comments
 (0)