   end
   def def_arity(_), do: nil
 
+
+
   @doc "Returns the name of the function/macro defined in the given `ast`"
   for op <- @def_ops do
     def def_name({unquote(op) = op, _, [{:when, _, fun_ast}, _]}) do

  end
  def def_name_with_op(_), do: nil

  @doc "Returns the name of the functions/macros for the given module's `ast`"
  def def_names(nil), do: []
  def def_names({:defmodule, _, _arguments} = ast) do
    ast
    |> Code.postwalk(&traverse_mod/2)
    |> Enum.map(&def_name/1)
    |> Enum.uniq
  end

  @doc "Returns the name of the functions/macros for the given module's `ast`"
  def def_names_with_op(nil), do: []
  def def_names_with_op({:defmodule, _, _arguments} = ast) do
    ast
    |> Code.postwalk(&traverse_mod/2)
    |> Enum.map(&def_name_with_op/1)
    |> Enum.uniq
  end

  @doc "Returns the name of the functions/macros for the given module's `ast` if it has the given `arity`."
  def def_names_with_op(nil, _arity), do: []
  def def_names_with_op({:defmodule, _, _arguments} = ast, arity) do
    ast
    |> Code.postwalk(&traverse_mod/2)
    |> Enum.map(&def_name_with_op(&1, arity))
    |> Enum.reject(&is_nil/1)
    |> Enum.uniq
  end

  @doc "Returns the list of modules used in a given module source code (dependent modules)"
  def modules({:defmodule, _, _arguments} = ast) do
    ast
    |> Code.postwalk(&find_dependent_modules/2)
    |> Enum.uniq
  end

  @doc "Returns the list of aliases defined in a given module source code"
  def aliases({:defmodule, _, _arguments} = ast) do
    ast
    |> Credo.Code.postwalk(&find_aliases/2)
    |> Enum.uniq
  end

  #
  # PRIVATE STUFF
  #

  # Single alias
  defp find_aliases({:alias, _, [{:__aliases__, _, mod_list}]} = ast, aliases) do
    module_names =
      mod_list
      |> Credo.Code.Name.full
      |> List.wrap

    {ast, aliases ++ module_names}
  end
  # Multi alias
  defp find_aliases({:alias, _, [{{:., _, [{:__aliases__, _, mod_list}, :{}]}, _, multi_mod_list}]} = ast, aliases) do
    module_names =
      Enum.map(multi_mod_list, fn(tuple) ->
        Credo.Code.Name.full([Credo.Code.Name.full(mod_list), Credo.Code.Name.full(tuple)])
      end)

    {ast, aliases ++ module_names}
  end
  defp find_aliases(ast, aliases) do
    {ast, aliases}
  end

  defp find_attribute({:@, _meta, arguments} = ast, tuple, attribute_name) do
    case List.first(arguments) do
      {^attribute_name, _meta, [value]} ->
        {:ok, value}
      _ ->
        {ast, tuple}
    end
  end
  defp find_attribute(ast, tuple, _name) do
    {ast, tuple}
  end

  # exclude module name

    end
  end

  defp find_attribute({:@, _meta, arguments} = ast, tuple, attribute_name) do
    case List.first(arguments) do
      {^attribute_name, _meta, [value]} ->
        {:ok, value}
      _ ->
        {ast, tuple}
    end
  end
  defp find_attribute(ast, tuple, _name) do
    {ast, tuple}
  end



  @doc "Returns the function/macro count for the given module's AST"
  def def_count(nil), do: 0
  def def_count({:defmodule, _, _arguments} = ast) do
    ast
    |> Code.postwalk(&collect_defs/2)
    |> Enum.count
  end

  def defs(nil), do: []
  def defs({:defmodule, _, _arguments} = ast) do
    Code.postwalk(ast, &collect_defs/2)
  end



  @doc "Returns the arity of the given function definition `ast`"
  for op <- @def_ops do
