Skip to content

Commit

Permalink
Enable use of unquote in defguard(p) (#13716)
Browse files Browse the repository at this point in the history
  • Loading branch information
alisinabh authored Jul 10, 2024
1 parent 797e503 commit 7a90b59
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 5 deletions.
20 changes: 15 additions & 5 deletions lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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) ->
Expand Down
12 changes: 12 additions & 0 deletions lib/elixir/test/elixir/kernel/guard_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7a90b59

Please sign in to comment.