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