Skip to content

Commit

Permalink
Copy callback typespecs
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtekmach committed Dec 21, 2017
1 parent 2728963 commit fccff5e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 16 deletions.
40 changes: 28 additions & 12 deletions lib/ex_doc/retriever.ex
Expand Up @@ -239,18 +239,9 @@ defmodule ExDoc.Retriever do
function = actual_def(name, arity, type)
line = find_actual_line(module_info.abst_code, function, :function) || doc_line

doc = docstring(doc, name, arity, Map.fetch(module_info.impls, {name, arity}))

specs = module_info.specs
|> Map.get(function, [])
|> Enum.map(&Typespec.spec_to_ast(name, &1))

specs =
if type == :defmacro do
Enum.map(specs, &remove_first_macro_arg/1)
else
specs
end
impl = Map.fetch(module_info.impls, {name, arity})
doc = docstring(doc, name, arity, impl)
specs = get_specs(module_info, type, function, name, arity, impl)

annotations =
case {type, name, arity} do
Expand Down Expand Up @@ -297,6 +288,31 @@ defmodule ExDoc.Retriever do
end
end

defp get_specs(module_info, type, function, name, arity, impl) do
specs =
module_info.specs
|> Map.get(function, [])
|> Enum.map(&Typespec.spec_to_ast(name, &1))

specs =
if type == :defmacro do
Enum.map(specs, &remove_first_macro_arg/1)
else
specs
end

copy_callback_specs(specs, name, arity, impl)
end

defp copy_callback_specs([], name, arity, {:ok, behaviour}) do
callbacks = Kernel.Typespec.beam_callbacks(behaviour)
{_, specs} = List.keyfind(callbacks, {name, arity}, 0)
Enum.map(specs, &Typespec.spec_to_ast(name, &1))
end
defp copy_callback_specs(specs, _, _, _) do
specs
end

defp docstring(nil, name, arity, {:ok, behaviour}) do
info = "Callback implementation for `c:#{inspect behaviour}.#{name}/#{arity}`."
callback_docs = Code.get_docs(behaviour, :callback_docs) || []
Expand Down
16 changes: 12 additions & 4 deletions test/ex_doc/retriever_test.exs
Expand Up @@ -230,10 +230,7 @@ defmodule ExDoc.RetrieverTest do
end

test "undocumented callback implementations get default doc" do
[module_node] =
["CustomBehaviourOne", "CustomBehaviourTwo", "CustomBehaviourImpl"]
|> docs_from_files()
|> Enum.filter(&match?(%ExDoc.ModuleNode{id: "CustomBehaviourImpl"}, &1))
[module_node] = docs_from_files ["CustomBehaviourImpl"]
docs = module_node.docs
assert Enum.map(docs, &(&1.id)) == ["bye/1", "greet/1", "hello/1"]
assert Enum.at(docs, 0).doc ==
Expand All @@ -245,6 +242,17 @@ defmodule ExDoc.RetrieverTest do
"Callback implementation for `c:CustomBehaviourOne.hello/1`."
end

test "callback implementations get specs from behaviour" do
[module_node] = docs_from_files ["CustomBehaviourImpl"]

[_, greet, hello] = module_node.docs
assert greet.name == :greet
assert Macro.to_string(greet.specs) == "[greet(integer() | String.t()) :: integer()]"

assert hello.name == :hello
assert Macro.to_string(hello.specs) == "[hello(non_neg_integer()) :: non_neg_integer()]"
end

## PROTOCOLS

test "docs_from_files properly tag protocols" do
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/behaviour.ex
Expand Up @@ -20,7 +20,9 @@ defmodule CustomBehaviourImpl do
@behaviour CustomBehaviourOne
@behaviour CustomBehaviourTwo

@spec hello(non_neg_integer()) :: non_neg_integer()
def hello(i), do: i

def greet(i), do: i

@doc "A doc for this so it doesn't use 'Callback implementation for'"
Expand Down

0 comments on commit fccff5e

Please sign in to comment.