From e2556fd4784ea46bfb46f6d3969688304d2d6025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 2 Oct 2024 08:47:09 +0200 Subject: [PATCH] Clarify behaviour of Macro.to_string on invalid ASTs Closes #13875. --- lib/elixir/lib/code/formatter.ex | 5 ++- lib/elixir/lib/macro.ex | 4 ++ .../code_normalizer/quoted_ast_test.exs | 28 ++++++++----- lib/elixir/test/elixir/macro_test.exs | 40 +------------------ 4 files changed, 27 insertions(+), 50 deletions(-) diff --git a/lib/elixir/lib/code/formatter.ex b/lib/elixir/lib/code/formatter.ex index 366a8d8b27..181ed9d118 100644 --- a/lib/elixir/lib/code/formatter.ex +++ b/lib/elixir/lib/code/formatter.ex @@ -614,8 +614,11 @@ defmodule Code.Formatter do end # #PID's and #Ref's may appear on regular AST + # Other foreign structures, such as maps and structs, + # may appear from Macro.to_string, so we stick a limit, + # although they won't be formatted accordingly. defp quoted_to_algebra(unknown, _context, state) do - {inspect(unknown), state} + {inspect(unknown, printable_limit: :infinity), state} end ## Blocks diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex index 5357903ed0..c3803470ff 100644 --- a/lib/elixir/lib/macro.ex +++ b/lib/elixir/lib/macro.ex @@ -1125,6 +1125,10 @@ defmodule Macro do `Code.quoted_to_algebra/2` as a lower level function with more control around formatting. + If the AST contains invalid nodes, this function will + attempt to inspect them, to aid debugging, although + the elements won't be formatted accordingly. + ## Examples iex> Macro.to_string(quote(do: foo.bar(1, 2, 3))) diff --git a/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs b/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs index 387e109e48..0f5db7f27b 100644 --- a/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs +++ b/lib/elixir/test/elixir/code_normalizer/quoted_ast_test.exs @@ -226,17 +226,6 @@ defmodule Code.Normalizer.QuotedASTTest do assert quoted_to_string(quoted) <> "\n" == expected end - test "invalid block" do - assert quoted_to_string({:__block__, [], {:bar, [], []}}) == - "{:__block__, [], {:bar, [], []}}" - - assert quoted_to_string({:foo, [], [{:do, :ok}, :not_keyword]}) == - "foo({:do, :ok}, :not_keyword)" - - assert quoted_to_string({:foo, [], [[{:do, :ok}, :not_keyword]]}) == - "foo([{:do, :ok}, :not_keyword])" - end - test "not in" do assert quoted_to_string(quote(do: false not in [])) == "false not in []" end @@ -738,6 +727,23 @@ defmodule Code.Normalizer.QuotedASTTest do end end + describe "quoted_to_algebra/2 with invalid" do + test "block" do + assert quoted_to_string({:__block__, [], {:bar, [], []}}) == + "{:__block__, [], {:bar, [], []}}" + + assert quoted_to_string({:foo, [], [{:do, :ok}, :not_keyword]}) == + "foo({:do, :ok}, :not_keyword)" + + assert quoted_to_string({:foo, [], [[{:do, :ok}, :not_keyword]]}) == + "foo([{:do, :ok}, :not_keyword])" + end + + test "ode" do + assert quoted_to_string(1..3) == "1..3" + end + end + describe "quoted_to_algebra/2 does not escape" do test "sigils" do assert quoted_to_string(quote(do: ~s/a\nb\tc/), escape: false) == ~S"~s/a\nb\tc/" diff --git a/lib/elixir/test/elixir/macro_test.exs b/lib/elixir/test/elixir/macro_test.exs index 89df500745..a131caf029 100644 --- a/lib/elixir/test/elixir/macro_test.exs +++ b/lib/elixir/test/elixir/macro_test.exs @@ -680,44 +680,8 @@ defmodule MacroTest do assert Macro.to_string(quote do: hello(world)) == "hello(world)" end - test "large number literals" do - # with quote - assert Macro.to_string(quote do: 576_460_752_303_423_455) == "576_460_752_303_423_455" - assert Macro.to_string(quote do: -576_460_752_303_423_455) == "-576_460_752_303_423_455" - - # without quote - assert Macro.to_string(576_460_752_303_423_455) == "576_460_752_303_423_455" - assert Macro.to_string(-576_460_752_303_423_455) == "-576_460_752_303_423_455" - end - - test "charlists" do - assert Macro.to_string(~c"foo") == ~s(~c"foo") - end - - defmodule HTML do - defstruct [:string] - - defimpl Inspect do - def inspect(%{string: string}, _) do - "~HTML[#{string}]" - end - end - end - - defmacro sigil_HTML({:<<>>, _, [string]}, []) do - Macro.escape(%HTML{string: string}) - end - - test "sigils" do - assert Macro.to_string(quote(do: ~HTML[hi])) == ~S/~HTML[hi]/ - - assert Macro.to_string( - quote do - ~HTML""" - hi - """ - end - ) == ~s[~HTML"""\nhi\n"""] + test "converts invalid AST with inspect" do + assert Macro.to_string(quote do: unquote(1..3)) == "1..3" end end