diff options
author | José Valim <jose.valim@plataformatec.com.br> | 2018-04-18 17:22:55 +0200 |
---|---|---|
committer | José Valim <jose.valim@plataformatec.com.br> | 2018-04-18 17:22:55 +0200 |
commit | c6bfb7b0c0908b271fad6a83c8d9c4f1481f3776 (patch) | |
tree | 6d6f48a2880031e9d244b3ec0845daa5a41094db | |
parent | adbd8da0f613dd807c6d637f81ec507b008cc745 (diff) | |
download | elixir-c6bfb7b0c0908b271fad6a83c8d9c4f1481f3776.tar.gz |
Consider hygienic vars in defguard, closes #7566
-rw-r--r-- | lib/elixir/lib/kernel/utils.ex | 28 | ||||
-rw-r--r-- | lib/elixir/test/elixir/kernel/guard_test.exs | 21 |
2 files changed, 41 insertions, 8 deletions
diff --git a/lib/elixir/lib/kernel/utils.ex b/lib/elixir/lib/kernel/utils.ex index 52242196d..9138c60f6 100644 --- a/lib/elixir/lib/kernel/utils.ex +++ b/lib/elixir/lib/kernel/utils.ex @@ -201,8 +201,8 @@ defmodule Kernel.Utils do defp extract_refs_from_args(args) do Macro.postwalk(args, [], fn - {ref, _meta, context} = var, acc when is_atom(ref) and is_atom(context) -> - {var, [{ref, context} | acc]} + {ref, meta, context} = var, acc when is_atom(ref) and is_atom(context) -> + {var, [{ref, var_context(meta, context)} | acc]} node, acc -> {node, acc} @@ -212,8 +212,8 @@ defmodule Kernel.Utils do # Finds every reference to `refs` in `guard` and wraps them in an unquote. defp unquote_every_ref(guard, refs) do Macro.postwalk(guard, fn - {ref, _meta, context} = var when is_atom(ref) and is_atom(context) -> - case {ref, context} in refs do + {ref, meta, context} = var when is_atom(ref) and is_atom(context) -> + case {ref, var_context(meta, context)} in refs do true -> literal_unquote(var) false -> var end @@ -227,9 +227,11 @@ defmodule Kernel.Utils do defp unquote_refs_once(guard, refs) do {_, used_refs} = Macro.postwalk(guard, [], fn - {ref, _meta, context} = var, acc when is_atom(ref) and is_atom(context) -> - case {ref, context} in refs and {ref, context} not in acc do - true -> {var, [{ref, context} | acc]} + {ref, meta, context} = var, acc when is_atom(ref) and is_atom(context) -> + pair = {ref, var_context(meta, context)} + + case pair in refs and pair not in acc do + true -> {var, [pair | acc]} false -> {var, acc} end @@ -237,7 +239,7 @@ defmodule Kernel.Utils do {node, acc} end) - vars = for {ref, context} <- :lists.reverse(used_refs), do: {ref, [], context} + vars = for {ref, context} <- :lists.reverse(used_refs), do: context_to_var(ref, context) exprs = for var <- vars, do: literal_unquote(var) quote do @@ -253,4 +255,14 @@ defmodule Kernel.Utils do defp literal_unquote(ast) do {:unquote, [], List.wrap(ast)} end + + defp context_to_var(ref, ctx) when is_atom(ctx), do: {ref, [], ctx} + defp context_to_var(ref, ctx) when is_integer(ctx), do: {ref, [counter: ctx], nil} + + defp var_context(meta, kind) do + case :lists.keyfind(:counter, 1, meta) do + {:counter, counter} -> counter + false -> kind + end + end end diff --git a/lib/elixir/test/elixir/kernel/guard_test.exs b/lib/elixir/test/elixir/kernel/guard_test.exs index cbf1d0899..e18e1e326 100644 --- a/lib/elixir/test/elixir/kernel/guard_test.exs +++ b/lib/elixir/test/elixir/kernel/guard_test.exs @@ -109,6 +109,27 @@ defmodule Kernel.GuardTest do end end + defmodule GuardFromMacro do + defmacro __using__(_) do + quote do + defguard is_even(value) when is_integer(value) and rem(value, 2) == 0 + end + end + end + + test "defguard defines a guard from inside another macro" do + defmodule UseGuardFromMacro do + use GuardFromMacro + + def assert! do + assert is_even(0) + refute is_even(1) + end + end + + UseGuardFromMacro.assert!() + end + defmodule IntegerPrivateGuards do defguardp is_even(value) when is_integer(value) and rem(value, 2) == 0 |