/
behaviour.ex
123 lines (98 loc) · 3.44 KB
/
behaviour.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
defmodule Behaviour do
@moduledoc """
Mechanism for handling behaviours.
This module is deprecated. Instead of `defcallback/1` and
`defmacrocallback/1`, the `@callback` and `@macrocallback`
module attributes can be used respectively. See the
documentation for `Module` for more information on these
attributes.
Instead of `MyModule.__behaviour__(:callbacks)`,
`MyModule.behaviour_info(:callbacks)` can be used.
"""
@moduledoc deprecated: "Use @callback and @macrocallback attributes instead"
@doc """
Defines a function callback according to the given type specification.
"""
@deprecated "Use the @callback module attribute instead"
defmacro defcallback(spec) do
do_defcallback(:def, split_spec(spec, quote(do: term)))
end
@doc """
Defines a macro callback according to the given type specification.
"""
@deprecated "Use the @macrocallback module attribute instead"
defmacro defmacrocallback(spec) do
do_defcallback(:defmacro, split_spec(spec, quote(do: Macro.t())))
end
defp split_spec({:when, _, [{:"::", _, [spec, return]}, guard]}, _default) do
{spec, return, guard}
end
defp split_spec({:when, _, [spec, guard]}, default) do
{spec, default, guard}
end
defp split_spec({:"::", _, [spec, return]}, _default) do
{spec, return, []}
end
defp split_spec(spec, default) do
{spec, default, []}
end
defp do_defcallback(kind, {spec, return, guards}) do
case Macro.decompose_call(spec) do
{name, args} ->
do_callback(kind, name, args, return, guards)
_ ->
raise ArgumentError, "invalid syntax in #{kind}callback #{Macro.to_string(spec)}"
end
end
defp do_callback(kind, name, args, return, guards) do
fun = fn
{:"::", _, [left, right]} ->
ensure_not_default(left)
ensure_not_default(right)
left
other ->
ensure_not_default(other)
other
end
:lists.foreach(fun, args)
spec =
quote do
unquote(name)(unquote_splicing(args)) :: unquote(return) when unquote(guards)
end
case kind do
:def -> quote(do: @callback(unquote(spec)))
:defmacro -> quote(do: @macrocallback(unquote(spec)))
end
end
defp ensure_not_default({:\\, _, [_, _]}) do
raise ArgumentError, "default arguments \\\\ not supported in defcallback/defmacrocallback"
end
defp ensure_not_default(_), do: :ok
@doc false
defmacro __using__(_) do
quote do
warning =
"the Behaviour module is deprecated. Instead of using this module, " <>
"use the @callback and @macrocallback module attributes. See the " <>
"documentation for Module for more information on these attributes"
IO.warn(warning)
@doc false
def __behaviour__(:callbacks) do
__MODULE__.behaviour_info(:callbacks)
end
def __behaviour__(:docs) do
{:docs_v1, _, :elixir, _, _, _, docs} = Code.fetch_docs(__MODULE__)
for {{kind, name, arity}, line, _, doc, _} <- docs, kind in [:callback, :macrocallback] do
case kind do
:callback -> {{name, arity}, line, :def, __behaviour__doc_value(doc)}
:macrocallback -> {{name, arity}, line, :defmacro, __behaviour__doc_value(doc)}
end
end
end
defp __behaviour__doc_value(%{"en" => doc}), do: doc
defp __behaviour__doc_value(:hidden), do: false
defp __behaviour__doc_value(_), do: nil
import unquote(__MODULE__)
end
end
end