diff --git a/lib/ex_doc/language.ex b/lib/ex_doc/language.ex
index 682ee47bf..eb84bee5f 100644
--- a/lib/ex_doc/language.ex
+++ b/lib/ex_doc/language.ex
@@ -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
@@ -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()],
diff --git a/lib/ex_doc/language/source.ex b/lib/ex_doc/language/source.ex
index dd23d319d..d9dc1a9e6 100644
--- a/lib/ex_doc/language/source.ex
+++ b/lib/ex_doc/language/source.ex
@@ -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 ->
@@ -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,
diff --git a/lib/ex_doc/refs.ex b/lib/ex_doc/refs.ex
index b09b3b391..bf5cc8b1a 100644
--- a/lib/ex_doc/refs.ex
+++ b/lib/ex_doc/refs.ex
@@ -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}]
diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex
index aaa30cfbf..e0f2c9c40 100644
--- a/lib/ex_doc/retriever.ex
+++ b/lib/ex_doc/retriever.ex
@@ -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
]
diff --git a/test/ex_doc/language/erlang_test.exs b/test/ex_doc/language/erlang_test.exs
index 2588a1347..2f1a415a7 100644
--- a/test/ex_doc/language/erlang_test.exs
+++ b/test/ex_doc/language/erlang_test.exs
@@ -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() :: t().|
+ end
+
test "tuple", c do
assert autolink_spec(~S"-spec foo() -> {ok, t()}.", c) ==
~s|foo() -> {ok, t()}.|
diff --git a/test/ex_doc/retriever/erlang_test.exs b/test/ex_doc/retriever/erlang_test.exs
index bcba5f3c6..5c475362b 100644
--- a/test/ex_doc/retriever/erlang_test.exs
+++ b/test/ex_doc/retriever/erlang_test.exs
@@ -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
@@ -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() :: atom()."
+
assert type1.id == "t:type1/0"
assert type1.type == :type
assert type1.group == :Types
@@ -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}"}
@@ -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() :: atom()."
+
assert type1.id == "t:type1/0"
assert type1.type == :type
assert type1.signature == "type1/0"