Skip to content

Commit 864e66d

Browse files
committed
Add correct line numbers to Erlang link warnings
1 parent a27be64 commit 864e66d

File tree

4 files changed

+89
-12
lines changed

4 files changed

+89
-12
lines changed

Diff for: 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

Diff for: 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

Diff for: 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)

Diff for: test/ex_doc/language/erlang_test.exs

+56-9
Original file line numberDiff line numberDiff line change
@@ -350,17 +350,55 @@ 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

@@ -668,9 +706,18 @@ defmodule ExDoc.Language.ErlangTest do
668706
|> ExDoc.DocAST.to_string()
669707
end
670708

671-
defp warn(fun) when is_function(fun, 0) do
709+
defp warn(fun, md \\ []) when is_function(fun, 0) do
672710
fun.()
673-
assert_received {:warn, message, _metadata}
711+
assert_received {:warn, message, metadata}
712+
713+
if Keyword.has_key?(md, :line) do
714+
assert md[:line] == metadata[:line]
715+
end
716+
717+
if Keyword.has_key?(md, :file) do
718+
assert md[:file] == metadata[:file]
719+
end
720+
674721
message
675722
end
676723

0 commit comments

Comments
 (0)