Skip to content

Commit

Permalink
Add support for nominal types
Browse files Browse the repository at this point in the history
This PR adds support for `-nominal` to be used in the same way as
`-type`. Without this change, OTP's CI can't build documentation
for nominal types.
  • Loading branch information
lucioleKi committed Nov 19, 2024
1 parent 1a5a3c9 commit e5ee357
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 8 deletions.
4 changes: 2 additions & 2 deletions lib/ex_doc/language.ex
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ defmodule ExDoc.Language do
The map has the following keys:
* `:type` - `:type` or `:opaque`
* `:type` - `:type` or `:opaque` or `:nominal`
* `:source_line` - the line where the code is located
Expand All @@ -122,7 +122,7 @@ defmodule ExDoc.Language do
"""
@callback type_data(entry :: tuple(), spec :: term()) ::
%{
type: :type | :opaque,
type: :type | :opaque | :nominal,
source_line: non_neg_integer(),
source_file: String.t() | nil,
signature: [binary()],
Expand Down
6 changes: 3 additions & 3 deletions lib/ex_doc/language/source.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ defmodule ExDoc.Language.Source do
end)
|> Map.new()

# Expand records in all specs, callbacks, types and opaques
# Expand records in all specs, callbacks, types, opaques and nominals
filtermap_ast(abst_code, nil, fn
{:attribute, anno, kind, {mfa, ast}} when kind in [:spec, :callback] ->
ast = Enum.map(ast, &expand_records(&1, records))
{:attribute, anno, kind, {mfa, ast}}

{:attribute, anno, type, {name, ast, args}} when type in [:opaque, :type] ->
{:attribute, anno, type, {name, ast, args}} when type in [:opaque, :nominal, :type] ->
{:attribute, anno, type, {name, expand_records(ast, records), args}}

otherwise ->
Expand Down Expand Up @@ -194,7 +194,7 @@ defmodule ExDoc.Language.Source do
def fetch_type!(module_data, name, arity) do
find_ast(module_data.private.abst_code, module_data.source_basedir, fn
{:attribute, anno, type, {^name, _, args} = spec} = attr ->
if type in [:opaque, :type] and length(args) == arity do
if type in [:nominal, :opaque, :type] and length(args) == arity do
%{
type: type,
spec: spec,
Expand Down
2 changes: 1 addition & 1 deletion lib/ex_doc/refs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ defmodule ExDoc.Refs do
[{{:module, module}, :limited}] ++
to_refs(exports(module), module, :function) ++
to_refs(callbacks(module), module, :callback) ++
to_refs(types(module, [:type, :opaque]), module, :type)
to_refs(types(module, [:type, :opaque, :nominal]), module, :type)
else
_ ->
[{{:module, module}, :undefined}]
Expand Down
2 changes: 1 addition & 1 deletion lib/ex_doc/retriever.ex
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ defmodule ExDoc.Retriever do
groups_for_docs =
config.groups_for_docs ++
[
Types: &(&1[:kind] in [:type, :opaque]),
Types: &(&1[:kind] in [:type, :opaque, :nominal]),
Callbacks: &(&1[:kind] in [:callback, :macrocallback]),
Functions: fn _ -> true end
]
Expand Down
5 changes: 5 additions & 0 deletions test/ex_doc/language/erlang_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,11 @@ defmodule ExDoc.Language.ErlangTest do
~s|foo(X, Y)|
end

test "nominal", c do
assert autolink_spec("-nominal foo() :: t().", c) ==
~s|foo() :: <a href="#t:t/0">t</a>().|
end

test "tuple", c do
assert autolink_spec(~S"-spec foo() -> {ok, t()}.", c) ==
~s|foo() -> {ok, <a href="#t:t/0">t</a>()}.|
Expand Down
26 changes: 25 additions & 1 deletion test/ex_doc/retriever/erlang_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,14 @@ defmodule ExDoc.Retriever.ErlangTest do
-doc("opaque1/0 docs.").
-opaque opaque1() :: atom().
-doc("nominal1/0 docs.").
-nominal nominal1() :: atom().
""")

config = %ExDoc.Config{source_url_pattern: "%{path}:%{line}"}
{[mod], []} = Retriever.docs_from_modules([:mod], config)
[equiv_type1, opaque1, type1] = mod.typespecs
[equiv_type1, opaque1, nominal1, type1] = mod.typespecs

assert opaque1.id == "t:opaque1/0"
assert opaque1.type == :opaque
Expand All @@ -301,6 +304,15 @@ defmodule ExDoc.Retriever.ErlangTest do
assert opaque1.spec |> Erlang.autolink_spec(current_kfa: {:type, :opaque1, 0}) ==
"opaque1()"

assert nominal1.id == "t:nominal1/0"
assert nominal1.type == :nominal
assert nominal1.group == :Types
assert nominal1.signature == "nominal1()"
assert nominal1.doc |> DocAST.to_string() =~ "nominal1/0 docs."

assert nominal1.spec |> Erlang.autolink_spec(current_kfa: {:type, :nominal1, 0}) ==
"nominal1() :: <a href=\"https://www.erlang.org/doc/apps/erts/erlang.html#t:atom/0\">atom</a>()."

assert type1.id == "t:type1/0"
assert type1.type == :type
assert type1.group == :Types
Expand Down Expand Up @@ -484,6 +496,9 @@ defmodule ExDoc.Retriever.ErlangTest do
-opaque opaque1() :: atom().
%% opaque1/0 docs.
-nominal nominal1() :: atom().
%% -doc("nominal1/0 docs.").
""")

config = %ExDoc.Config{source_url_pattern: "%{path}:%{line}"}
Expand All @@ -498,6 +513,15 @@ defmodule ExDoc.Retriever.ErlangTest do
assert opaque1.spec |> Erlang.autolink_spec(current_kfa: {:type, :opaque1, 0}) ==
"opaque1()"

assert nominal1.id == "t:nominal1/0"
assert nominal1.type == :nominal
assert nominal1.group == :Types
assert nominal1.signature == "nominal1/0"
assert nominal1.doc |> DocAST.to_string() =~ "nominal1/0 docs."

assert nominal1.spec |> Erlang.autolink_spec(current_kfa: {:type, :nominal1, 0}) ==
"nominal1() :: <a href=\"https://www.erlang.org/doc/apps/erts/erlang.html#t:atom/0\">atom</a>()."

assert type1.id == "t:type1/0"
assert type1.type == :type
assert type1.signature == "type1/0"
Expand Down

0 comments on commit e5ee357

Please sign in to comment.