Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a72f81f

Browse files
committedNov 15, 2023
Add correct line numbers to Erlang link warnings
1 parent a27be64 commit a72f81f

File tree

4 files changed

+110
-21
lines changed

4 files changed

+110
-21
lines changed
 

‎lib/ex_doc/autolink.ex

+14-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ defmodule ExDoc.Autolink do
77
# * `:current_module` - the module that the docs are being generated for. Used to link local
88
# calls and see if remote calls are in the same app.
99
#
10+
# * `:current_kfa` - the kind, function, arity that the docs are being generated for. Is nil
11+
# if there is no such thing. Used to generate more accurate warnings.
12+
#
1013
# * `:module_id` - id of the module being documented (e.g.: `"String"`)
1114
#
1215
# * `:file` - source file location
@@ -48,6 +51,7 @@ defmodule ExDoc.Autolink do
4851
extras: [],
4952
deps: [],
5053
ext: ".html",
54+
current_kfa: nil,
5155
siblings: [],
5256
skip_undefined_reference_warnings_on: [],
5357
skip_code_autolink_to: [],
@@ -495,7 +499,16 @@ defmodule ExDoc.Autolink do
495499
# TODO: Remove on Elixir v1.14
496500
stacktrace_info =
497501
if unquote(Version.match?(System.version(), ">= 1.14.0")) do
498-
[file: config.file, line: config.line]
502+
f =
503+
case config.current_kfa do
504+
{:function, f, a} ->
505+
[function: {f, a}]
506+
507+
_ ->
508+
[]
509+
end
510+
511+
[file: config.file, line: config.line, module: config.current_module] ++ f
499512
else
500513
[]
501514
end

‎lib/ex_doc/formatter/html.ex

+17-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,15 @@ defmodule ExDoc.Formatter.HTML do
9292
docs =
9393
for child_node <- node.docs do
9494
id = id(node, child_node)
95-
autolink_opts = autolink_opts ++ [id: id, line: child_node.doc_line]
95+
96+
autolink_opts =
97+
autolink_opts ++
98+
[
99+
id: id,
100+
line: child_node.doc_line,
101+
current_kfa: {:function, child_node.name, child_node.arity}
102+
]
103+
96104
specs = Enum.map(child_node.specs, &language.autolink_spec(&1, autolink_opts))
97105
child_node = %{child_node | specs: specs}
98106
render_doc(child_node, language, autolink_opts, opts)
@@ -101,7 +109,14 @@ defmodule ExDoc.Formatter.HTML do
101109
typespecs =
102110
for child_node <- node.typespecs do
103111
id = id(node, child_node)
104-
autolink_opts = autolink_opts ++ [id: id, line: child_node.doc_line]
112+
113+
autolink_opts =
114+
autolink_opts ++
115+
[
116+
id: id,
117+
line: child_node.doc_line,
118+
current_kfa: {child_node.type, child_node.name, child_node.arity}
119+
]
105120

106121
child_node = %{
107122
child_node

‎lib/ex_doc/language/erlang.ex

+2
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ defmodule ExDoc.Language.Erlang do
262262
end
263263

264264
defp walk_doc({:code, attrs, [code], meta} = ast, config) when is_binary(code) do
265+
config = %{config | line: meta[:line]}
266+
265267
case Autolink.url(code, :regular_link, config) do
266268
url when is_binary(url) ->
267269
code = remove_prefix(code)

‎test/ex_doc/language/erlang_test.exs

+77-18
Original file line numberDiff line numberDiff line change
@@ -350,21 +350,59 @@ defmodule ExDoc.Language.ErlangTest do
350350
describe "autolink_doc/2 for markdown warnings" do
351351
@describetag warnings: :send
352352

353+
test "bad function in module", c do
354+
assert warn(
355+
fn ->
356+
assert autolink_doc("\n`erlang_bar:bad/0`", c) ==
357+
~s|<code class="inline">erlang_bar:bad/0</code>|
358+
end,
359+
line: 2
360+
) =~
361+
~s|documentation references function "erlang_bar:bad/0" but it is undefined or private|
362+
end
363+
364+
test "bad type in module", c do
365+
assert warn(
366+
fn ->
367+
assert autolink_doc("\n`t:erlang_bar:bad/0`", c) ==
368+
~s|<code class="inline">t:erlang_bar:bad/0</code>|
369+
end,
370+
line: 2
371+
) =~
372+
~s|documentation references type "t:erlang_bar:bad/0" but it is undefined or private|
373+
end
374+
375+
test "bad callback in module", c do
376+
assert warn(
377+
fn ->
378+
assert autolink_doc("\n`c:erlang_bar:bad/0`", c) ==
379+
~s|<code class="inline">c:erlang_bar:bad/0</code>|
380+
end,
381+
line: 2
382+
) =~ ~s|documentation references callback "c:erlang_bar:bad/0" but it is undefined|
383+
end
384+
353385
test "bad function in module ref", c do
354-
assert warn(fn ->
355-
assert autolink_doc("[Bad](`bad/0`)", c) == ~s|Bad|
356-
end) =~ ~s|documentation references function "bad/0" but it is undefined or private|
386+
assert warn(
387+
fn ->
388+
assert autolink_doc("[Bad](`bad/0`)", c) == ~s|Bad|
389+
end,
390+
line: nil
391+
) =~ ~s|documentation references function "bad/0" but it is undefined or private|
357392
end
358393

359394
test "linking to local extra works does not work", c do
360-
assert warn(fn ->
361-
assert autolink_doc("[extra](`e:extra.md`)", c) ==
362-
~s|extra|
363-
end) =~ ~r/documentation references "e:extra.md" but it is invalid/
395+
assert warn(
396+
fn ->
397+
assert autolink_doc("[extra](`e:extra.md`)", c) ==
398+
~s|extra|
399+
end,
400+
line: nil
401+
) =~ ~r/documentation references "e:extra.md" but it is invalid/
364402
end
365403
end
366404

367-
describe "autolink_edoc/2 for extra" do
405+
describe "autolink_doc/2 for extra" do
368406
test "function", c do
369407
assert autolink_extra("`erlang_foo:foo/0`", c) ==
370408
~s|<a href="erlang_foo.html#foo/0"><code class="inline">erlang_foo:foo/0</code></a>|
@@ -410,9 +448,16 @@ defmodule ExDoc.Language.ErlangTest do
410448
~s|<code class="inline">...a/0</code>|
411449
end
412450

413-
test "bad type", c do
414-
assert autolink_extra("`t:bad:bad/0`", c) ==
415-
~s|<code class="inline">t:bad:bad/0</code>|
451+
@tag warnings: :send
452+
test "bad type ref", c do
453+
assert warn(
454+
fn ->
455+
assert autolink_extra("[t](`t:bad:bad/0`)", c) ==
456+
~s|t|
457+
end,
458+
file: "extra.md",
459+
line: nil
460+
) =~ ~s|documentation references type "t:bad:bad/0" but it is undefined or private|
416461
end
417462

418463
test "bad module", c do
@@ -422,10 +467,14 @@ defmodule ExDoc.Language.ErlangTest do
422467

423468
@tag warnings: :send
424469
test "bad module using m:", c do
425-
assert warn(fn ->
426-
assert autolink_extra("`m:does_not_exist`", c) ==
427-
~s|<code class="inline">m:does_not_exist</code>|
428-
end) =~ ~r|documentation references module \"does_not_exist\" but it is undefined|
470+
assert warn(
471+
fn ->
472+
assert autolink_extra("`m:does_not_exist`", c) ==
473+
~s|<code class="inline">m:does_not_exist</code>|
474+
end,
475+
file: "extra.md",
476+
line: 1
477+
) =~ ~r|documentation references module \"does_not_exist\" but it is undefined|
429478
end
430479

431480
test "extras" do
@@ -618,7 +667,7 @@ defmodule ExDoc.Language.ErlangTest do
618667
[{:p, _, [ast], _}] = ExDoc.Markdown.to_ast(text, [])
619668

620669
opts = c |> Map.take([:warnings]) |> Enum.to_list()
621-
do_autolink_doc(ast, opts)
670+
do_autolink_doc(ast, [file: "extra.md"] ++ opts)
622671
end
623672

624673
defp autolink_doc(text, c, opts \\ []) do
@@ -637,6 +686,7 @@ defmodule ExDoc.Language.ErlangTest do
637686
ast,
638687
[
639688
current_module: :erlang_foo,
689+
file: "erlang_foo.erl",
640690
module_id: "erlang_foo",
641691
deps: [foolib: "https://foolib.com"]
642692
] ++
@@ -668,9 +718,18 @@ defmodule ExDoc.Language.ErlangTest do
668718
|> ExDoc.DocAST.to_string()
669719
end
670720

671-
defp warn(fun) when is_function(fun, 0) do
721+
defp warn(fun, md \\ []) when is_function(fun, 0) do
672722
fun.()
673-
assert_received {:warn, message, _metadata}
723+
assert_received {:warn, message, metadata}
724+
725+
if Keyword.has_key?(md, :line) do
726+
assert md[:line] == metadata[:line]
727+
end
728+
729+
if Keyword.has_key?(md, :file) do
730+
assert md[:file] == metadata[:file]
731+
end
732+
674733
message
675734
end
676735

0 commit comments

Comments
 (0)
Please sign in to comment.