Implement a simple version of the t helper for the interactive shell #609

Merged
merged 1 commit into from Nov 1, 2012
@@ -237,6 +237,83 @@ defmodule Kernel.Typespec do
quote do: []
end
+ @doc """
+ Retrieves all type specifications from a module.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def types(module) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :type, ti} inlist abstract_code, do: {:type, ti}
+ [] ->
+ []
+ end
+ end
+
+ @doc """
+ Retrieves all type specifications from a module with a given name and arity.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def types(module, type, arity) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :type, {t, _, a} = ti} inlist abstract_code, t == type, length(a) == arity, do: {:type, ti}
+ _ ->
+ []
+ end
+ end
+
+
+ @doc """
+ Retrieves all functions specifications from a module.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def specs(module) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :spec, {fa, s}} inlist abstract_code, do: {{:spec, fa}, s}
+ _ ->
+ []
+ end
+ end
+
+ @doc """
+ Retrieves all functions specifications from a module for a given name and arity.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def specs(module, function, arity) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :spec, {{f,a}=fa, s}} inlist abstract_code, f == function, a == arity, do: {{:spec, fa}, s}
+ _ ->
+ []
+ end
+ end
+
+
+ defp abstract_code(module) do
+ case :beam_lib.chunks(abstract_code_beam(module), [:abstract_code]) do
+ {:ok, {_, [{:abstract_code, {raw_abstract_v1, abstract_code}}]}} ->
+ {:ok, abstract_code}
+ _ ->
+ :error
+ end
+ end
+
+ defp abstract_code_beam(module) when is_atom(module) do
+ case :code.which(module) do
+ :non_existing -> module
+ file -> file
+ end
+ end
+ defp abstract_code_beam(binary) when is_binary(binary) do
+ binary
+ end
+
## Typespec conversion
# Handle unions
@@ -328,4 +328,32 @@ defmodule Typespec.Test.Type do
assert Kernel.Typespec.to_binary(spec) == Macro.to_binary(definition)
end
end
+
+ # test/spec retrieval
+
+ test "specs retrieval" do
+ Code.compiler_options debug_info: true
+ { :module, T, binary, _ } = defmodule T do
+ @spec a, do: any
+ def a, do: nil
+ end
+ Code.compiler_options debug_info: false
+ :code.delete(T)
+ :code.purge(T)
+ assert [{{:spec,{:a,_}},[{:type,_,:fun,[{:type,_,:product,[]},{:type,_,:any,[]}]}]}] = Kernel.Typespec.specs(binary)
+ assert [{{:spec,{:a,_}},[{:type,_,:fun,[{:type,_,:product,[]},{:type,_,:any,[]}]}]}] = Kernel.Typespec.specs(binary, :a, 0)
+ end
+
+ test "types retrieval" do
+ Code.compiler_options debug_info: true
+ { :module, T, binary, _ } = defmodule T do
+ @type a :: any
+ end
+ Code.compiler_options debug_info: false
+ :code.delete(T)
+ :code.purge(T)
+ assert [{:type, {:a,{:type,_,:any,[]},[]}}] = Kernel.Typespec.types(binary)
+ assert [{:type, {:a,{:type,_,:any,[]},[]}}] = Kernel.Typespec.types(binary, :a, 0)
+ end
+
end
@@ -13,6 +13,7 @@ defmodule IEx.Helpers do
* `c/2` - compiles a file in the given path
* `h/0`,`h/1`, `h/2` - prints help/documentation
+ * `t/1`,`t/3` — prints type specification information
* `m/0` - prints loaded modules
* `r/0` - recompiles and reloads the given module's source file
* `v/0` - prints all commands and values
@@ -213,6 +214,70 @@ defmodule IEx.Helpers do
atom_to_binary(var)
end
+ # FIXME: this can't be shown as h(t/1)
+ @doc """
+ Prints all types and function specifications from a given module
+
+ ## Examples
+
+ t(Enum)
+
+ """
+ def __t_module__(module) do
+ types =
+ lc type inlist Kernel.Typespec.types(module) do
+ IO.puts Kernel.Typespec.to_binary(type)
+ type
+ end
+ specs =
+ lc spec inlist Kernel.Typespec.specs(module) do
+ IO.puts Kernel.Typespec.to_binary(spec)
+ spec
+ end
+ if specs == [] and types == [] do
+ IO.puts "No type specifications for #{inspect module} has been found"
+ end
+ :ok
+ end
+
+ @doc """
+ Prints the type specification for a given function or a type
+
+ ## Examples
+
+ t(Enum.all?/2)
+ t(Enum.t/0)
+
+ """
+ def t(module, function, arity) do
+ types =
+ lc type inlist Kernel.Typespec.types(module, function, arity) do
+ IO.puts Kernel.Typespec.to_binary(type)
+ type
+ end
+ specs =
+ lc spec inlist Kernel.Typespec.specs(module, function, arity) do
+ IO.puts Kernel.Typespec.to_binary(spec)
+ spec
+ end
+ if specs == [] and types == [] do
+ IO.puts "No type specification for #{inspect module}.#{function}/#{arity} has been found"
+ end
+ :ok
+ end
+
+ defmacro t({ :/, _, [{ { :., _, [mod, fun] }, _, [] }, arity] }) do
+ quote do
+ t(unquote(mod), unquote(fun), unquote(arity))
+ end
+ end
+ defmacro t(module) do
+ quote do
+ __t_module__(unquote(module))
+ end
+ end
+
+
@doc """
Retrieves nth query's value from the history. Use negative
values to lookup query's value from latest to earliest.