From e5ee3577d04f81a54df065cd47f4dceba76549fb Mon Sep 17 00:00:00 2001 From: lucioleKi Date: Tue, 19 Nov 2024 14:07:45 +0100 Subject: [PATCH] Add support for nominal types 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. --- lib/ex_doc/language.ex | 4 ++-- lib/ex_doc/language/source.ex | 6 +++--- lib/ex_doc/refs.ex | 2 +- lib/ex_doc/retriever.ex | 2 +- test/ex_doc/language/erlang_test.exs | 5 +++++ test/ex_doc/retriever/erlang_test.exs | 26 +++++++++++++++++++++++++- 6 files changed, 37 insertions(+), 8 deletions(-) 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"