Skip to content

Commit 960e2ef

Browse files
Warn if typespec references filtered module (#1761)
1 parent 6645809 commit 960e2ef

File tree

15 files changed

+125
-68
lines changed

15 files changed

+125
-68
lines changed

lib/ex_doc.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ defmodule ExDoc do
2121
ExDoc.Markdown.put_markdown_processor(processor)
2222
end
2323

24-
docs = config.retriever.docs_from_dir(config.source_beam, config)
25-
find_formatter(config.formatter).run(docs, config)
24+
{module_nodes, filtered_nodes} = config.retriever.docs_from_dir(config.source_beam, config)
25+
find_formatter(config.formatter).run({module_nodes, filtered_nodes}, config)
2626
end
2727

2828
# Short path for programmatic interface

lib/ex_doc/autolink.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ defmodule ExDoc.Autolink do
2222
# * `:skip_undefined_reference_warnings_on` - list of modules to skip the warning on
2323
#
2424
# * `:skip_code_autolink_to` - list of terms that will be skipped when autolinking (e.g: "PrivateModule")
25+
#
26+
# * `:filtered_modules` - A list of module nodes that were filtered by the retriever
2527

2628
defstruct [
2729
:current_module,
@@ -35,7 +37,8 @@ defmodule ExDoc.Autolink do
3537
ext: ".html",
3638
siblings: [],
3739
skip_undefined_reference_warnings_on: [],
38-
skip_code_autolink_to: []
40+
skip_code_autolink_to: [],
41+
filtered_modules: []
3942
]
4043

4144
@hexdocs "https://hexdocs.pm/"

lib/ex_doc/formatter/epub.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ defmodule ExDoc.Formatter.EPUB do
99
Generate EPUB documentation for the given modules.
1010
"""
1111
@spec run(list, ExDoc.Config.t()) :: String.t()
12-
def run(project_nodes, config) when is_map(config) do
12+
def run(project_nodes, config) when is_list(project_nodes) and is_map(config) do
13+
run({project_nodes, []}, config)
14+
end
15+
16+
def run({project_nodes, filtered_modules}, config) when is_map(config) do
1317
parent = config.output
1418
config = normalize_config(config)
1519

@@ -19,7 +23,8 @@ defmodule ExDoc.Formatter.EPUB do
1923
&create_output_dir(&1, config)
2024
)
2125

22-
project_nodes = HTML.render_all(project_nodes, ".xhtml", config, highlight_tag: "samp")
26+
project_nodes =
27+
HTML.render_all(project_nodes, filtered_modules, ".xhtml", config, highlight_tag: "samp")
2328

2429
nodes_map = %{
2530
modules: HTML.filter_list(:module, project_nodes),

lib/ex_doc/formatter/html.ex

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,24 @@ defmodule ExDoc.Formatter.HTML do
1010
@doc """
1111
Generate HTML documentation for the given modules.
1212
"""
13-
@spec run(list, ExDoc.Config.t()) :: String.t()
14-
def run(project_nodes, config) when is_map(config) do
13+
@spec run(
14+
[ExDoc.ModuleNode.t()]
15+
| {[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]},
16+
ExDoc.Config.t()
17+
) :: String.t()
18+
19+
def run(project_nodes, config) when is_list(project_nodes) and is_map(config) do
20+
run({project_nodes, []}, config)
21+
end
22+
23+
def run({project_nodes, filtered_modules}, config) when is_map(config) do
1524
config = normalize_config(config)
1625
config = %{config | output: Path.expand(config.output)}
1726

1827
build = Path.join(config.output, ".build")
1928
setup_output(config.output, &cleanup_output_dir(&1, config), &create_output_dir(&1, config))
2029

21-
project_nodes = render_all(project_nodes, ".html", config, [])
30+
project_nodes = render_all(project_nodes, filtered_modules, ".html", config, [])
2231
extras = build_extras(config, ".html")
2332

2433
# Generate search early on without api reference in extras
@@ -64,14 +73,15 @@ defmodule ExDoc.Formatter.HTML do
6473
@doc """
6574
Autolinks and renders all docs.
6675
"""
67-
def render_all(project_nodes, ext, config, opts) do
76+
def render_all(project_nodes, filtered_modules, ext, config, opts) do
6877
base = [
6978
apps: config.apps,
7079
deps: config.deps,
7180
ext: ext,
7281
extras: extra_paths(config),
7382
skip_undefined_reference_warnings_on: config.skip_undefined_reference_warnings_on,
74-
skip_code_autolink_to: config.skip_code_autolink_to
83+
skip_code_autolink_to: config.skip_code_autolink_to,
84+
filtered_modules: filtered_modules
7585
]
7686

7787
project_nodes

lib/ex_doc/language/elixir.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,12 +783,16 @@ defmodule ExDoc.Language.Elixir do
783783
(\(.*\)) # Arguments <rest />
784784
}x
785785

786-
Regex.replace(regex, string, fn _all, call_string, module_string, name_string, rest ->
786+
Regex.replace(regex, string, fn all, call_string, module_string, name_string, rest ->
787787
module = string_to_module(module_string)
788788
name = String.to_atom(name_string)
789789
arity = count_args(rest, 0, 0)
790790
original_text = call_string <> "()"
791791

792+
if Enum.any?(config.filtered_modules, &(&1.id == module_string)) do
793+
warn("Typespec references filtered module: #{all}", {config.file, config.line}, config.id)
794+
end
795+
792796
url =
793797
if module do
794798
remote_url({:type, module, name, arity}, config, original_text)

lib/ex_doc/nodes.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ defmodule ExDoc.ModuleNode do
2222
source_url: nil,
2323
type: nil,
2424
language: nil,
25-
annotations: []
25+
annotations: [],
26+
metadata: nil
2627

2728
@typep annotation :: atom()
2829

@@ -46,7 +47,8 @@ defmodule ExDoc.ModuleNode do
4647
source_url: String.t() | nil,
4748
type: atom(),
4849
language: module(),
49-
annotations: [annotation()]
50+
annotations: [annotation()],
51+
metadata: map()
5052
}
5153
end
5254

lib/ex_doc/retriever.ex

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ defmodule ExDoc.Retriever do
1212

1313
@doc """
1414
Extract documentation from all modules in the specified directory or directories.
15+
16+
Returns a tuple containing `{modules, filtered}`, using `config.filter_modules`
17+
as a filter criteria.
1518
"""
16-
@spec docs_from_dir(Path.t() | [Path.t()], ExDoc.Config.t()) :: [ExDoc.ModuleNode.t()]
19+
@spec docs_from_dir(Path.t() | [Path.t()], ExDoc.Config.t()) ::
20+
{[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]}
1721
def docs_from_dir(dir, config) when is_binary(dir) do
1822
files = Path.wildcard(Path.expand("*.beam", dir))
1923

@@ -28,15 +32,32 @@ defmodule ExDoc.Retriever do
2832
end
2933

3034
@doc """
31-
Extract documentation from all modules in the list `modules`
35+
Extract documentation from all modules and returns a tuple containing
36+
`{modules, filtered}`, two lists of modules that were extracted and filtered
37+
by `config.filter_modules`, respectively.
3238
"""
33-
@spec docs_from_modules([atom], ExDoc.Config.t()) :: [ExDoc.ModuleNode.t()]
39+
@spec docs_from_modules([atom], ExDoc.Config.t()) ::
40+
{[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]}
3441
def docs_from_modules(modules, config) when is_list(modules) do
3542
modules
36-
|> Enum.flat_map(&get_module(&1, config))
43+
|> Enum.reduce({[], []}, fn module_name, {modules, filtered} = acc ->
44+
case get_module(module_name, config) do
45+
{:error, _module} ->
46+
acc
47+
48+
{:ok, module_node} ->
49+
if config.filter_modules.(module_node.module, module_node.metadata),
50+
do: {[module_node | modules], filtered},
51+
else: {modules, [module_node | filtered]}
52+
end
53+
end)
3754
|> sort_modules(config)
3855
end
3956

57+
defp sort_modules({modules, filtered}, config) do
58+
{sort_modules(modules, config), sort_modules(filtered, config)}
59+
end
60+
4061
defp sort_modules(modules, config) when is_list(modules) do
4162
Enum.sort_by(modules, fn module ->
4263
{GroupMatcher.group_index(config.groups_for_modules, module.group), module.nested_context,
@@ -50,14 +71,13 @@ defmodule ExDoc.Retriever do
5071
end
5172

5273
defp get_module(module, config) do
53-
with {:docs_v1, _, language, _, _, metadata, _} = docs_chunk <- docs_chunk(module),
54-
true <- config.filter_modules.(module, metadata),
74+
with {:docs_v1, _, language, _, _, _metadata, _} = docs_chunk <- docs_chunk(module),
5575
{:ok, language} <- ExDoc.Language.get(language, module),
5676
%{} = module_data <- language.module_data(module, docs_chunk, config) do
57-
[generate_node(module, module_data, config)]
77+
{:ok, generate_node(module, module_data, config)}
5878
else
5979
_ ->
60-
[]
80+
{:error, module}
6181
end
6282
end
6383

@@ -142,7 +162,8 @@ defmodule ExDoc.Retriever do
142162
source_path: source_path,
143163
source_url: source_link(source, module_data.line),
144164
language: module_data.language,
145-
annotations: List.wrap(metadata[:tags])
165+
annotations: List.wrap(metadata[:tags]),
166+
metadata: metadata
146167
}
147168
end
148169

test/ex_doc/formatter/epub/templates_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ defmodule ExDoc.Formatter.EPUB.TemplatesTest do
2727

2828
defp get_module_page(names, config \\ []) do
2929
config = doc_config(config)
30-
mods = ExDoc.Retriever.docs_from_modules(names, config)
31-
[mod | _] = HTML.render_all(mods, ".xhtml", config, highlight_tag: "samp")
30+
{mods, []} = ExDoc.Retriever.docs_from_modules(names, config)
31+
[mod | _] = HTML.render_all(mods, [], ".xhtml", config, highlight_tag: "samp")
3232
Templates.module_page(config, mod)
3333
end
3434

test/ex_doc/formatter/html/search_data_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ defmodule ExDoc.Formatter.HTML.SearchDataTest do
243243
end
244244

245245
defp search_data(modules, config) do
246-
modules = ExDoc.Retriever.docs_from_modules(modules, config)
246+
{modules, []} = ExDoc.Retriever.docs_from_modules(modules, config)
247247

248248
ExDoc.Formatter.HTML.run(modules, config)
249249
[path] = Path.wildcard(Path.join([config.output, "dist", "search_data-*.js"]))

test/ex_doc/formatter/html/templates_test.exs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
3131

3232
defp get_module_page(names, context, config \\ []) do
3333
config = doc_config(context, config)
34-
mods = ExDoc.Retriever.docs_from_modules(names, config)
35-
[mod | _] = HTML.render_all(mods, ".html", config, [])
34+
{mods, []} = ExDoc.Retriever.docs_from_modules(names, config)
35+
[mod | _] = HTML.render_all(mods, [], ".html", config, [])
3636
Templates.module_page(mod, @empty_nodes_map, config)
3737
end
3838

@@ -264,7 +264,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
264264

265265
test "outputs listing for the given nodes", context do
266266
names = [CompiledWithDocs, CompiledWithDocs.Nested]
267-
nodes = ExDoc.Retriever.docs_from_modules(names, doc_config(context))
267+
{nodes, []} = ExDoc.Retriever.docs_from_modules(names, doc_config(context))
268268

269269
assert [
270270
%{
@@ -293,7 +293,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
293293

294294
test "outputs deprecated: true if node is deprecated", context do
295295
names = [CompiledWithDocs]
296-
nodes = ExDoc.Retriever.docs_from_modules(names, doc_config(context))
296+
{nodes, []} = ExDoc.Retriever.docs_from_modules(names, doc_config(context))
297297

298298
path = ["modules", Access.at!(0), "nodeGroups", Access.at!(0), "nodes"]
299299
sidebar_functions = get_in(create_sidebar_items(%{modules: nodes}, []), path)
@@ -306,7 +306,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
306306

307307
test "outputs deprecated: true if module is deprecated", context do
308308
names = [Warnings]
309-
nodes = ExDoc.Retriever.docs_from_modules(names, doc_config(context))
309+
{nodes, []} = ExDoc.Retriever.docs_from_modules(names, doc_config(context))
310310

311311
assert Enum.any?(
312312
create_sidebar_items(%{modules: nodes}, [])["modules"],
@@ -315,7 +315,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
315315
end
316316

317317
test "outputs nodes grouped based on metadata", context do
318-
nodes =
318+
{nodes, []} =
319319
ExDoc.Retriever.docs_from_modules(
320320
[CompiledWithDocs, CompiledWithDocs.Nested],
321321
doc_config(context,
@@ -361,7 +361,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
361361
test "outputs module groups for the given nodes", context do
362362
names = [CompiledWithDocs, CompiledWithDocs.Nested]
363363
group_mapping = [groups_for_modules: [Group: [CompiledWithDocs]]]
364-
nodes = ExDoc.Retriever.docs_from_modules(names, doc_config(context, group_mapping))
364+
{nodes, []} = ExDoc.Retriever.docs_from_modules(names, doc_config(context, group_mapping))
365365

366366
assert [
367367
%{"group" => ""},
@@ -420,8 +420,8 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
420420
test "builds sections out of moduledocs", context do
421421
names = [CompiledWithDocs, CompiledWithoutDocs, DuplicateHeadings]
422422
config = doc_config(context)
423-
nodes = ExDoc.Retriever.docs_from_modules(names, config)
424-
nodes = HTML.render_all(nodes, ".html", config, [])
423+
{nodes, []} = ExDoc.Retriever.docs_from_modules(names, config)
424+
nodes = HTML.render_all(nodes, [], ".html", config, [])
425425

426426
[compiled_with_docs, compiled_without_docs, duplicate_headings] =
427427
create_sidebar_items(%{modules: nodes}, [])["modules"]

test/ex_doc/language/elixir_test.exs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,16 @@ defmodule ExDoc.Language.ElixirTest do
439439
warn(~m"`c:InMemory.unknown/0`")
440440
end
441441

442+
test "warning if typespec references filtered module" do
443+
ExDoc.Refs.insert([
444+
{{:module, AutolinkTest.Keep}, :public},
445+
{{:function, AutolinkTest.Filtered}, :public},
446+
{{:type, AutolinkTest.Filtered, :type, 0}, :public}
447+
])
448+
449+
# TODO: testing
450+
end
451+
442452
test "warnings" do
443453
ExDoc.Refs.insert([
444454
{{:module, AutolinkTest.Foo}, :public},

0 commit comments

Comments
 (0)