diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex index 0fe6f227dc9..3cb671a1d5b 100644 --- a/lib/elixir/lib/kernel.ex +++ b/lib/elixir/lib/kernel.ex @@ -5765,8 +5765,11 @@ defmodule Kernel do "only a single when clause is allowed" {call, impls} -> - case Macro.decompose_call(call) do - {_name, args} -> + case decompose_args(call) do + :error -> + raise ArgumentError, "invalid syntax in defguard #{Macro.to_string(call)}" + + args -> validate_variable_only_args!(call, args) macro_definition = @@ -5788,13 +5791,20 @@ defmodule Kernel do Elixir.Kernel.@(doc(guard: true)) unquote(macro_definition) end - - _invalid_definition -> - raise ArgumentError, "invalid syntax in defguard #{Macro.to_string(call)}" end end end + defp decompose_args({name, _, args}) when is_atom(name) and is_atom(args), do: [] + + defp decompose_args({name, _, args}) when is_atom(name) and is_list(args), do: args + + defp decompose_args({{:unquote, _, _}, _, args}) when is_atom(args), do: [] + + defp decompose_args({{:unquote, _, _}, _, args}) when is_list(args), do: args + + defp decompose_args(_), do: :error + defp validate_variable_only_args!(call, args) do Enum.each(args, fn {ref, _meta, context} when is_atom(ref) and is_atom(context) -> diff --git a/lib/elixir/test/elixir/kernel/guard_test.exs b/lib/elixir/test/elixir/kernel/guard_test.exs index 18a2e0654c8..731cc796c5e 100644 --- a/lib/elixir/test/elixir/kernel/guard_test.exs +++ b/lib/elixir/test/elixir/kernel/guard_test.exs @@ -60,6 +60,18 @@ defmodule Kernel.GuardTest do refute MacrosInGuards.is_foobar(:baz) end + defmodule UnquotedInGuardCall do + @value :foo + + defguard unquote(String.to_atom("is_#{@value}"))(x) when x == unquote(@value) + end + + test "guards names can be defined dynamically using unquote" do + require UnquotedInGuardCall + assert UnquotedInGuardCall.is_foo(:foo) + refute UnquotedInGuardCall.is_foo(:bar) + end + defmodule GuardsInGuards do defguard is_foo(atom) when atom == :foo defguard is_foobar(atom) when is_foo(atom) or atom == :bar