Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add Macro.extract_args, closes #677

  • Loading branch information...
commit c06a44c2fdb120a7660335c440ded407e4bd0bbf 1 parent 4e8fb99
@josevalim josevalim authored
View
7 lib/elixir/lib/behaviour.ex
@@ -59,7 +59,12 @@ defmodule Behaviour do
end
defp do_defcallback(fun, return, caller) do
- { name, args } = :elixir_clauses.extract_args(fun)
+ case Macro.extract_args(fun) do
+ { name, args } -> :ok
+ :error ->
+ raise ArgumentError, message: "invalid syntax in defcallback #{Macro.to_binary(fun)}"
+ end
+
arity = length(args)
Enum.each args, fn
View
5 lib/elixir/lib/kernel.ex
@@ -2942,7 +2942,10 @@ defmodule Kernel do
append_first = Keyword.get(opts, :append_first, false)
lc fun inlist funs do
- { name, args } = :elixir_clauses.extract_args(fun)
+ case Macro.extract_args(fun) do
+ { name, args } -> :ok
+ :error -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_binary(fun)}"
+ end
actual_args =
case append_first and args != [] do
View
23 lib/elixir/lib/macro.ex
@@ -30,6 +30,28 @@ defmodule Macro do
end
@doc """
+ Receives an expresion representing a possible definition
+ and extracts its arguments. It returns a tuple with the
+ function name and the arguments list or `:error` if not
+ a valid call syntax.
+
+ This is useful for macros that want to provide the same
+ arguments syntax available in def/defp/defmacro and friends.
+
+ ## Examples
+
+ extract_args(quote do: foo) == { :foo, [] }
+ extract_args(quote do: foo()) == { :foo, [] }
+ extract_args(quote do: :foo.()) == { :foo, [] }
+ extract_args(quote do: foo(1,2,3)) == { :foo, [1,2,3] }
+ extract_args(quote do: 1.(1,2,3)) == :error
+
+ """
+ def extract_args(expr) do
+ :elixir_clauses.extract_args(expr)
+ end
+
+ @doc """
Recursively escapes the given value so it can be inserted
into a syntax tree. Structures that are valid syntax nodes
(like atoms, integers, binaries) are represented by themselves.
@@ -334,6 +356,7 @@ defmodule Macro do
* Macros (local or remote);
* Aliases are expanded (if possible) and return atoms;
* All pseudo-variables (__FILE__, __MODULE__, etc);
+ * Module attributes reader (@foo);
In case the expression cannot be expanded, it returns the expression itself.
View
3  lib/elixir/src/elixir_clauses.erl
@@ -75,7 +75,8 @@ extract_or_clauses(Term, Acc) -> [Term|Acc].
extract_args({ { '.', _, [Name] }, _, Args }) when is_atom(Name), is_list(Args) -> { Name, Args };
extract_args({ Name, _, Args }) when is_atom(Name), is_atom(Args) -> { Name, [] };
-extract_args({ Name, _, Args }) when is_atom(Name), is_list(Args) -> { Name, Args }.
+extract_args({ Name, _, Args }) when is_atom(Name), is_list(Args) -> { Name, Args };
+extract_args(_) -> error.
% Extract guards when it is in the last element of the args
View
10 lib/elixir/src/elixir_macros.erl
@@ -221,13 +221,21 @@ translate({Kind, Line, [Call]}, S) when ?FUNS(Kind) ->
translate({Kind, Line, [Call, Expr]}, S) when ?FUNS(Kind) ->
assert_module_scope(Line, Kind, S),
assert_no_function_scope(Line, Kind, S),
+
{ TCall, Guards } = elixir_clauses:extract_guards(Call),
- { Name, Args } = elixir_clauses:extract_args(TCall),
+ { Name, Args } = case elixir_clauses:extract_args(TCall) of
+ error -> syntax_error(Line, S#elixir_scope.file,
+ "invalid syntax in ~s ~s", [Kind, 'Elixir.Macro':to_binary(TCall)]);
+ Tuple -> Tuple
+ end,
+
assert_no_aliases_name(Line, Name, Args, S),
+
TName = elixir_tree_helpers:abstract_syntax(Name),
TArgs = elixir_tree_helpers:abstract_syntax(Args),
TGuards = elixir_tree_helpers:abstract_syntax(Guards),
TExpr = elixir_tree_helpers:abstract_syntax(Expr),
+
{ elixir_def:wrap_definition(Kind, Line, TName, TArgs, TGuards, TExpr, S), S };
translate({Kind, Line, [Name, Args, Guards, Expr]}, S) when ?FUNS(Kind) ->
View
5 lib/elixir/test/elixir/kernel/errors_test.exs
@@ -167,6 +167,11 @@ defmodule Kernel.ErrorsTest do
format_rescue 'defmodule Foo, do: (defmodule Elixir.Foo, do: true)'
end
+ test :invalid_definition do
+ assert "nofile:1: invalid syntax in def 1.(hello)" ==
+ format_rescue 'defmodule Foo, do: (def 1.(hello), do: true)'
+ end
+
test :duplicated_bitstring_size do
assert "nofile:1: duplicated size definition for bitstring" == format_rescue '<<1 :: [size(12), size(13)]>>'
end
View
14 lib/elixir/test/elixir/macro_test.exs
@@ -141,6 +141,10 @@ defmodule MacroTest do
assert Macro.to_binary(quote do: foo.bar.([1, 2, 3])) == "foo.bar().([1, 2, 3])"
end
+ test :atom_call_to_binary do
+ assert Macro.to_binary(quote do: :foo.(1, 2, 3)) == ":foo.(1, 2, 3)"
+ end
+
test :aliases_call_to_binary do
assert Macro.to_binary(quote do: Foo.Bar.baz(1, 2, 3)) == "Foo.Bar.baz(1, 2, 3)"
assert Macro.to_binary(quote do: Foo.Bar.baz([1, 2, 3])) == "Foo.Bar.baz([1, 2, 3])"
@@ -267,4 +271,14 @@ defmodule MacroTest do
assert Macro.safe_term(quote do: [1+1]) == { :unsafe, quote do: 1 + 1 }
assert Macro.safe_term(quote do: {1+1}) == { :unsafe, quote do: 1 + 1 }
end
+
+ ## extract_args
+
+ test :extract_args do
+ assert Macro.extract_args(quote do: foo) == { :foo, [] }
+ assert Macro.extract_args(quote do: foo()) == { :foo, [] }
+ assert Macro.extract_args(quote do: :foo.()) == { :foo, [] }
+ assert Macro.extract_args(quote do: foo(1,2,3)) == { :foo, [1,2,3] }
+ assert Macro.extract_args(quote do: 1.(1,2,3)) == :error
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.