Skip to content

Commit

Permalink
Implement dbg/2 for "if" expressions (#13603)
Browse files Browse the repository at this point in the history
Co-authored-by: Jean Klingler <[email protected]>
  • Loading branch information
dkuku and Jean Klingler authored May 29, 2024
1 parent 967c87b commit 7cf57ec
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 6 deletions.
47 changes: 41 additions & 6 deletions lib/elixir/lib/macro.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2552,13 +2552,13 @@ defmodule Macro do
header = dbg_format_header(env)

quote do
to_debug = unquote(dbg_ast_to_debuggable(code))
to_debug = unquote(dbg_ast_to_debuggable(code, env))
unquote(__MODULE__).__dbg__(unquote(header), to_debug, unquote(options))
end
end

# Pipelines.
defp dbg_ast_to_debuggable({:|>, _meta, _args} = pipe_ast) do
defp dbg_ast_to_debuggable({:|>, _meta, _args} = pipe_ast, _env) do
value_var = unique_var(:value, __MODULE__)
values_acc_var = unique_var(:values, __MODULE__)

Expand Down Expand Up @@ -2591,7 +2591,7 @@ defmodule Macro do
dbg_decomposed_binary_operators = [:&&, :||, :and, :or]

# Logic operators.
defp dbg_ast_to_debuggable({op, _meta, [_left, _right]} = ast)
defp dbg_ast_to_debuggable({op, _meta, [_left, _right]} = ast, _env)
when op in unquote(dbg_decomposed_binary_operators) do
acc_var = unique_var(:acc, __MODULE__)
result_var = unique_var(:result, __MODULE__)
Expand All @@ -2603,7 +2603,7 @@ defmodule Macro do
end
end

defp dbg_ast_to_debuggable({:case, _meta, [expr, [do: clauses]]} = ast) do
defp dbg_ast_to_debuggable({:case, _meta, [expr, [do: clauses]]} = ast, _env) do
clauses_returning_index =
Enum.with_index(clauses, fn {:->, meta, [left, right]}, index ->
{:->, meta, [left, {right, index}]}
Expand All @@ -2621,7 +2621,7 @@ defmodule Macro do
end
end

defp dbg_ast_to_debuggable({:cond, _meta, [[do: clauses]]} = ast) do
defp dbg_ast_to_debuggable({:cond, _meta, [[do: clauses]]} = ast, _env) do
modified_clauses =
Enum.with_index(clauses, fn {:->, _meta, [[left], right]}, index ->
hd(
Expand All @@ -2642,8 +2642,26 @@ defmodule Macro do
end
end

defp dbg_ast_to_debuggable({:if, meta, [condition_ast, clauses]} = ast, env) do
case Macro.Env.lookup_import(env, {:if, 2}) do
[macro: Kernel] ->
condition_result_var = unique_var(:condition_result, __MODULE__)

quote do
unquote(condition_result_var) = unquote(condition_ast)
result = unquote({:if, meta, [condition_result_var, clauses]})

{:if, unquote(escape(ast)), unquote(escape(condition_ast)),
unquote(condition_result_var), result}
end

_ ->
quote do: {:value, unquote(escape(ast)), unquote(ast)}
end
end

# Any other AST.
defp dbg_ast_to_debuggable(ast) do
defp dbg_ast_to_debuggable(ast, _env) do
quote do: {:value, unquote(escape(ast)), unquote(ast)}
end

Expand Down Expand Up @@ -2755,6 +2773,23 @@ defmodule Macro do
{formatted, value}
end

defp dbg_format_ast_to_debug(
{:if, ast, condition_ast, condition_result, result},
options
) do
formatted = [
dbg_maybe_underline("If condition", options),
":\n",
dbg_format_ast_with_value(condition_ast, condition_result, options),
?\n,
dbg_maybe_underline("If expression", options),
":\n",
dbg_format_ast_with_value(ast, result, options)
]

{formatted, result}
end

defp dbg_format_ast_to_debug({:value, code_ast, value}, options) do
{dbg_format_ast_with_value(code_ast, value, options), value}
end
Expand Down
82 changes: 82 additions & 0 deletions lib/elixir/test/elixir/macro_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ defmodule Macro.ExternalTest do
end
end

defmodule CustomIf do
def if(_cond, _expr) do
"custom if result"
end
end

defmodule MacroTest do
use ExUnit.Case, async: true
doctest Macro
Expand Down Expand Up @@ -536,6 +542,82 @@ defmodule MacroTest do
"""
end

test "if statement" do
x = true
map = %{a: 5, b: 1}

{result, formatted} =
dbg_format(
if true and x do
map[:a] * 2
else
map[:b]
end
)

assert result == 10

assert formatted =~ "macro_test.exs"

assert formatted =~ """
If condition:
true and x #=> true
If expression:
if true and x do
map[:a] * 2
else
map[:b]
end #=> 10
"""
end

test "if statement without else" do
x = true
map = %{a: 5, b: 1}

{result, formatted} =
dbg_format(
if false and x do
map[:a] * 2
end
)

assert result == nil

assert formatted =~ "macro_test.exs"

assert formatted =~ """
If condition:
false and x #=> false
If expression:
if false and x do
map[:a] * 2
end #=> nil
"""
end

test "custom if definition" do
import Kernel, except: [if: 2]
import CustomIf, only: [if: 2]

{result, formatted} =
dbg_format(
if true do
"something"
end
)

assert result == "custom if result"

assert formatted =~ """
if true do
"something"
end #=> "custom if result"
"""
end

test "with \"syntax_colors: []\" it doesn't print any color sequences" do
{_result, formatted} = dbg_format("hello")
refute formatted =~ "\e["
Expand Down

0 comments on commit 7cf57ec

Please sign in to comment.