From cbaf2afab04b2ceeecbebfdac777ad7a31ac0e0c Mon Sep 17 00:00:00 2001 From: Eksperimental Date: Tue, 18 Aug 2020 08:16:47 -0500 Subject: [PATCH 1/3] Elixir name space and prefix --- lib/ex_doc/autolink.ex | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/ex_doc/autolink.ex b/lib/ex_doc/autolink.ex index d46aa6dd2..4c45c57ef 100644 --- a/lib/ex_doc/autolink.ex +++ b/lib/ex_doc/autolink.ex @@ -487,15 +487,29 @@ defmodule ExDoc.Autolink do end end - defp fragment(:ex_doc, kind, name, arity) do - prefix = - case kind do - :function -> "" - :callback -> "c:" - :type -> "t:" - end + defp strip_elixir_namespace(module) when is_atom(module), + do: strip_elixir_namespace("#{module}") + + defp strip_elixir_namespace("Elixir." <> rest), do: rest + defp strip_elixir_namespace("c:Elixir." <> rest), do: "c:" <> rest + defp strip_elixir_namespace("t:Elixir." <> rest), do: "t:" <> rest + defp strip_elixir_namespace(rest) when is_binary(rest), do: rest - "#" <> prefix <> "#{T.enc(Atom.to_string(name))}/#{arity}" + defp prefix(kind) + defp prefix(:function), do: "" + defp prefix(:callback), do: "c:" + defp prefix(:type), do: "t:" + + defp ref_id({:module, module}) do + strip_elixir_namespace(module) + end + + defp ref_id({kind, module, name, arity}) do + prefix(kind) <> "#{strip_elixir_namespace(module)}.#{name}/#{arity}" + end + + defp fragment(:ex_doc, kind, name, arity) do + "#" <> prefix(kind) <> "#{T.enc(Atom.to_string(name))}/#{arity}" end defp fragment(:otp, kind, name, arity) do From e9ed687a515d9bd2ebc409ce90507078994cdf5d Mon Sep 17 00:00:00 2001 From: Eksperimental Date: Tue, 14 Jul 2020 16:05:04 -0500 Subject: [PATCH 2/3] Introduce :skip_reference_warnings_to option Closes #1192 Addresses https://github.com/elixir-lang/ex_doc/pull/1151#issuecomment-646730079 --- lib/ex_doc/autolink.ex | 39 +++++++++++++-- lib/ex_doc/cli.ex | 3 +- lib/ex_doc/config.ex | 2 + lib/ex_doc/formatter/html.ex | 2 + lib/mix/tasks/docs.ex | 9 ++-- test/ex_doc/formatter/epub_test.exs | 3 +- test/ex_doc/formatter/html_test.exs | 76 ++++++++++++++++++++++++++++- test/fixtures/warnings.ex | 18 +++++++ 8 files changed, 142 insertions(+), 10 deletions(-) diff --git a/lib/ex_doc/autolink.ex b/lib/ex_doc/autolink.ex index 4c45c57ef..b3f03291e 100644 --- a/lib/ex_doc/autolink.ex +++ b/lib/ex_doc/autolink.ex @@ -22,7 +22,9 @@ defmodule ExDoc.Autolink do # # * `:extras` - list of extras # - # * `:skip_undefined_reference_warnings_on` - list of modules to skip the warning on + # * `:skip_reference_warnings_to` - list of references and/or files, to skip warning when calling the given reference + # + # * `:skip_undefined_reference_warnings_on` - list of references and/or files to skip the warning on @enforce_keys [:app, :file] @@ -36,6 +38,7 @@ defmodule ExDoc.Autolink do extras: [], ext: ".html", siblings: [], + skip_reference_warnings_to: [], skip_undefined_reference_warnings_on: [] ] @@ -50,6 +53,18 @@ defmodule ExDoc.Autolink do def doc(ast, options \\ []) do config = struct!(__MODULE__, options) + + config = + config + |> Map.update!( + :skip_reference_warnings_to, + &Enum.map(&1, fn x -> strip_elixir_namespace(x) end) + ) + |> Map.update!( + :skip_undefined_reference_warnings_on, + &Enum.map(&1, fn x -> strip_elixir_namespace(x) end) + ) + walk(ast, config) end @@ -544,11 +559,29 @@ defmodule ExDoc.Autolink do end defp maybe_warn(ref, config, visibility, metadata) do - skipped = config.skip_undefined_reference_warnings_on file = Path.relative_to(config.file, File.cwd!()) + file_path = Map.get(metadata, :file_path, nil) line = config.line + skip_on = config.skip_undefined_reference_warnings_on + skip_to = config.skip_reference_warnings_to + ref_list_on = [config.id, config.module_id, file] + + ref_list_to = + cond do + match?({_, _, _, _}, ref) or match?({:module, _}, ref) -> + [ref_id(ref)] + + file_path != nil -> + [file_path] - unless Enum.any?([config.id, config.module_id, file], &(&1 in skipped)) do + true -> + [] + end + + if Enum.any?(ref_list_on, &(&1 in skip_on)) or + Enum.any?(ref_list_to, &(&1 in skip_to)) do + nil + else warn(ref, {file, line}, config.id, visibility, metadata) end end diff --git a/lib/ex_doc/cli.ex b/lib/ex_doc/cli.ex index ad64b7519..0a234e3e4 100644 --- a/lib/ex_doc/cli.ex +++ b/lib/ex_doc/cli.ex @@ -139,7 +139,8 @@ defmodule ExDoc.CLI do groups_for_functions: [ Guards: & &1[:guard] == true ], - skip_undefined_reference_warnings_on: ["compatibility-and-deprecations"], + skip_reference_warnings_to: ["t:ModuleWithDocFalse.element/0"], + skip_undefined_reference_warnings_on: ["lib/elixir/pages/compatibility-and-deprecations.md"], groups_for_modules: [ ... ] diff --git a/lib/ex_doc/config.ex b/lib/ex_doc/config.ex index 0a6cdbd08..4ff72dd3e 100644 --- a/lib/ex_doc/config.ex +++ b/lib/ex_doc/config.ex @@ -44,6 +44,7 @@ defmodule ExDoc.Config do title: nil, version: nil, authors: nil, + skip_reference_warnings_to: [], skip_undefined_reference_warnings_on: [] @type t :: %__MODULE__{ @@ -80,6 +81,7 @@ defmodule ExDoc.Config do title: nil | String.t(), version: nil | String.t(), authors: nil | [String.t()], + skip_reference_warnings_to: [String.t()], skip_undefined_reference_warnings_on: [String.t()] } end diff --git a/lib/ex_doc/formatter/html.ex b/lib/ex_doc/formatter/html.ex index 917ca8131..1149124cc 100644 --- a/lib/ex_doc/formatter/html.ex +++ b/lib/ex_doc/formatter/html.ex @@ -73,6 +73,7 @@ defmodule ExDoc.Formatter.HTML do current_module: node.module, ext: ext, extras: extra_paths(config), + skip_reference_warnings_to: config.skip_reference_warnings_to, skip_undefined_reference_warnings_on: config.skip_undefined_reference_warnings_on, module_id: node.id, file: node.source_path, @@ -289,6 +290,7 @@ defmodule ExDoc.Formatter.HTML do file: input, ext: ext, extras: extra_paths(config), + skip_reference_warnings_to: config.skip_reference_warnings_to, skip_undefined_reference_warnings_on: config.skip_undefined_reference_warnings_on ] diff --git a/lib/mix/tasks/docs.ex b/lib/mix/tasks/docs.ex index db4b3a7fb..e508bf053 100644 --- a/lib/mix/tasks/docs.ex +++ b/lib/mix/tasks/docs.ex @@ -156,10 +156,13 @@ defmodule Mix.Tasks.Docs do * `:ignore_apps` - Apps to be ignored when generating documentation in an umbrella project. Receives a list of atoms. Example: `[:first_app, :second_app]`. - * `:skip_undefined_reference_warnings_on` - ExDoc warns when it can't create a `Mod.fun/arity` + * `:skip_reference_warnings_to` - Ignores warnings emitted when a given reference or file is linked to; default: `[]`. + + * `:skip_undefined_reference_warnings_on` - Ignores warnings emitted within a given reference or file. ExDoc warns when it can't create a `Mod.fun/arity` reference in the current project docs e.g. because of a typo. This list controls where to - skip the warnings, for a given module/function/callback/type (e.g.: `["Foo", "Bar.baz/0"]`) - or on a given file (e.g.: `["pages/deprecations.md"]`); default: `[]`. + skip the warnings for a given module/function/callback/type defined in the listed references (e.g.: `["Foo", "Bar.baz/0"]`) + or on a give file (e.g.: `["pages/deprecations.md"]`); default: `[]`. + ## Groups diff --git a/test/ex_doc/formatter/epub_test.exs b/test/ex_doc/formatter/epub_test.exs index ae006bbec..64b2e441c 100644 --- a/test/ex_doc/formatter/epub_test.exs +++ b/test/ex_doc/formatter/epub_test.exs @@ -30,7 +30,8 @@ defmodule ExDoc.Formatter.EPUBTest do source_root: beam_dir(), source_beam: beam_dir(), extras: ["test/fixtures/README.md"], - skip_undefined_reference_warnings_on: ["Warnings"] + skip_reference_warnings_to: [], + skip_undefined_reference_warnings_on: ["Warnings", "Warnings.Submodule"] ] end diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index caf182f3b..b0f20c747 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -53,7 +53,11 @@ defmodule ExDoc.Formatter.HTMLTest do end defp generate_docs(config) do - config = Keyword.put_new(config, :skip_undefined_reference_warnings_on, ["Warnings"]) + config = + config + |> Keyword.put_new(:skip_reference_warnings_to, []) + |> Keyword.put_new(:skip_undefined_reference_warnings_on, ["Warnings", "Warnings.Submodule"]) + ExDoc.generate_docs(config[:project], config[:version], config) end @@ -121,13 +125,81 @@ defmodule ExDoc.Formatter.HTMLTest do output = capture_io(:stderr, fn -> generate_docs( - doc_config(skip_undefined_reference_warnings_on: ["test/fixtures/warnings.ex"]) + doc_config( + skip_undefined_reference_warnings_on: [ + "test/fixtures/warnings.ex" + ] + ) ) end) assert output == "" end + describe "skip_reference_warnings_to" do + test "emit warnings" do + output = + capture_io(:stderr, fn -> + generate_docs(doc_config(skip_undefined_reference_warnings_on: ["Warnings"])) + end) + + assert output =~ ~r{documentation references file \"unknown.md\" but it does not exist\n} + assert output =~ ~r{documentation references module \"UknownModule\" but it is undefined\n} + + assert output =~ + ~r{documentation references module \"Elixir.UknownModule\" but it is undefined\n} + + assert output =~ + ~r{documentation references \"CompiledWithDocs.function/0\" but it is undefined or private\n} + + assert output =~ + ~r{documentation references \"c:CompiledWithDocs.callback/1\" but it is undefined\n} + + assert output =~ + ~r{documentation references \"t:CompiledWithDocs.type/2\" but it is undefined or private\n} + end + + test "skip warnings" do + output = + capture_io(:stderr, fn -> + generate_docs( + doc_config( + skip_reference_warnings_to: [ + "unknown.md", + "UknownModule", + "CompiledWithDocs.function/0", + "c:CompiledWithDocs.callback/1", + "t:CompiledWithDocs.type/2" + ], + skip_undefined_reference_warnings_on: ["Warnings"] + ) + ) + end) + + assert output == "" + end + + test "skip warnings withing Elixir namespace" do + output = + capture_io(:stderr, fn -> + generate_docs( + doc_config( + skip_reference_warnings_to: [ + "unknown.md", + "Elixir.UknownModule", + "Elixir.CompiledWithDocs.function/0", + "c:Elixir.CompiledWithDocs.callback/1", + "t:Elixir.CompiledWithDocs.type/2" + ], + skip_undefined_reference_warnings_on: ["Warnings"] + ) + ) + end) + + assert output == "" + end + end + test "generates headers for index.html and module pages" do generate_docs(doc_config(main: "RandomError")) content_index = File.read!("#{output_dir()}/index.html") diff --git a/test/fixtures/warnings.ex b/test/fixtures/warnings.ex index b24798719..8b68365ec 100644 --- a/test/fixtures/warnings.ex +++ b/test/fixtures/warnings.ex @@ -20,3 +20,21 @@ defmodule Warnings do """ def foo(), do: :ok end + +defmodule Warnings.Submodule do + @moduledoc """ + Refer to: + - [this file](unknown.md) + - [this module](`UknownModule`) + - [this function](`CompiledWithDocs.function/0`) + - [this callback](`c:CompiledWithDocs.callback/1`) + - [this type](`t:CompiledWithDocs.type/2`) + + Refer to with Elixir namespace: + - [this module](`Elixir.UknownModule`) + - [this function](`Elixir.CompiledWithDocs.function/0`) + - [this callback](`c:Elixir.CompiledWithDocs.callback/1`) + - [this type](`t:Elixir.CompiledWithDocs.type/2`) + + """ +end From ae049729c4c4447b9a0b4fa84ae16343f7ef2899 Mon Sep 17 00:00:00 2001 From: Eksperimental Date: Wed, 19 Aug 2020 08:05:37 -0500 Subject: [PATCH 3/3] Fix typo in module name --- test/ex_doc/formatter/html_test.exs | 8 ++++---- test/fixtures/warnings.ex | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index b0f20c747..3165547b6 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -144,10 +144,10 @@ defmodule ExDoc.Formatter.HTMLTest do end) assert output =~ ~r{documentation references file \"unknown.md\" but it does not exist\n} - assert output =~ ~r{documentation references module \"UknownModule\" but it is undefined\n} + assert output =~ ~r{documentation references module \"UnknownModule\" but it is undefined\n} assert output =~ - ~r{documentation references module \"Elixir.UknownModule\" but it is undefined\n} + ~r{documentation references module \"Elixir.UnknownModule\" but it is undefined\n} assert output =~ ~r{documentation references \"CompiledWithDocs.function/0\" but it is undefined or private\n} @@ -166,7 +166,7 @@ defmodule ExDoc.Formatter.HTMLTest do doc_config( skip_reference_warnings_to: [ "unknown.md", - "UknownModule", + "UnknownModule", "CompiledWithDocs.function/0", "c:CompiledWithDocs.callback/1", "t:CompiledWithDocs.type/2" @@ -186,7 +186,7 @@ defmodule ExDoc.Formatter.HTMLTest do doc_config( skip_reference_warnings_to: [ "unknown.md", - "Elixir.UknownModule", + "Elixir.UnknownModule", "Elixir.CompiledWithDocs.function/0", "c:Elixir.CompiledWithDocs.callback/1", "t:Elixir.CompiledWithDocs.type/2" diff --git a/test/fixtures/warnings.ex b/test/fixtures/warnings.ex index 8b68365ec..6efc45c64 100644 --- a/test/fixtures/warnings.ex +++ b/test/fixtures/warnings.ex @@ -25,13 +25,13 @@ defmodule Warnings.Submodule do @moduledoc """ Refer to: - [this file](unknown.md) - - [this module](`UknownModule`) + - [this module](`UnknownModule`) - [this function](`CompiledWithDocs.function/0`) - [this callback](`c:CompiledWithDocs.callback/1`) - [this type](`t:CompiledWithDocs.type/2`) Refer to with Elixir namespace: - - [this module](`Elixir.UknownModule`) + - [this module](`Elixir.UnknownModule`) - [this function](`Elixir.CompiledWithDocs.function/0`) - [this callback](`c:Elixir.CompiledWithDocs.callback/1`) - [this type](`t:Elixir.CompiledWithDocs.type/2`)