Skip to content

Commit 7db4413

Browse files
committed
Add source field to diagnostics
1 parent 4d52d18 commit 7db4413

File tree

14 files changed

+85
-25
lines changed

14 files changed

+85
-25
lines changed

lib/elixir/lib/code.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,22 @@ defmodule Code do
197197
@typedoc """
198198
Diagnostics returned by the compiler and code evaluation.
199199
200+
The file and position relate to where the diagnostic should be shown.
200201
If there is a file and position, then the diagnostic is precise
201202
and you can use the given file and position for generating snippets,
202203
IDEs annotations, and so on. An optional span is available with
203204
the line and column the diagnostic ends.
204205
205206
Otherwise, a stacktrace may be given, which you can place your own
206207
heuristics to provide better reporting.
208+
209+
The source field points to the source file the compiler tracked
210+
the error to. For example, a file `lib/foo.ex` may embed `.eex`
211+
templates from `lib/foo/bar.eex`. A syntax error on the EEx template
212+
will point to file `lib/foo/bar.eex` but the source is `lib/foo.ex`.
207213
"""
208214
@type diagnostic(severity) :: %{
215+
required(:source) => Path.t() | nil,
209216
required(:file) => Path.t() | nil,
210217
required(:severity) => severity,
211218
required(:message) => String.t(),

lib/elixir/lib/kernel/parallel_compiler.ex

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -859,9 +859,12 @@ defmodule Kernel.ParallelCompiler do
859859
)
860860

861861
for {file, _, description, stacktrace} <- deadlock do
862+
file = Path.absname(file)
863+
862864
%{
863865
severity: :error,
864-
file: Path.absname(file),
866+
file: file,
867+
source: file,
865868
position: nil,
866869
message: description,
867870
stacktrace: stacktrace,
@@ -889,13 +892,14 @@ defmodule Kernel.ParallelCompiler do
889892
])
890893
end
891894

892-
defp to_error(file, kind, reason, stack) do
893-
{line, span} = get_line_span(file, reason, stack)
894-
file = Path.absname(file)
895+
defp to_error(source, kind, reason, stack) do
896+
{file, line, span} = get_snippet_info(source, reason, stack)
897+
source = Path.absname(source)
895898
message = :unicode.characters_to_binary(Kernel.CLI.format_error(kind, reason, stack))
896899

897900
%{
898-
file: file,
901+
file: file || source,
902+
source: source,
899903
position: line || 0,
900904
message: message,
901905
severity: :error,
@@ -905,47 +909,47 @@ defmodule Kernel.ParallelCompiler do
905909
}
906910
end
907911

908-
defp get_line_span(
912+
defp get_snippet_info(
909913
_file,
910-
%{line: line, column: column, end_line: end_line, end_column: end_column},
914+
%{file: file, line: line, column: column, end_line: end_line, end_column: end_column},
911915
_stack
912916
)
913917
when is_integer(line) and line > 0 and is_integer(column) and column >= 0 and
914918
is_integer(end_line) and end_line > 0 and is_integer(end_column) and end_column >= 0 do
915-
{{line, column}, {end_line, end_column}}
919+
{Path.absname(file), {line, column}, {end_line, end_column}}
916920
end
917921

918-
defp get_line_span(_file, %{line: line, column: column}, _stack)
922+
defp get_snippet_info(_file, %{file: file, line: line, column: column}, _stack)
919923
when is_integer(line) and line > 0 and is_integer(column) and column >= 0 do
920-
{{line, column}, nil}
924+
{Path.absname(file), {line, column}, nil}
921925
end
922926

923-
defp get_line_span(_file, %{line: line}, _stack) when is_integer(line) and line > 0 do
924-
{line, nil}
927+
defp get_snippet_info(_file, %{line: line}, _stack) when is_integer(line) and line > 0 do
928+
{nil, line, nil}
925929
end
926930

927-
defp get_line_span(file, :undef, [{_, _, _, []}, {_, _, _, info} | _]) do
928-
get_line_span_from_stacktrace_info(info, file)
931+
defp get_snippet_info(file, :undef, [{_, _, _, []}, {_, _, _, info} | _]) do
932+
get_snippet_info_from_stacktrace_info(info, file)
929933
end
930934

931-
defp get_line_span(file, _reason, [{_, _, _, [file: expanding]}, {_, _, _, info} | _])
935+
defp get_snippet_info(file, _reason, [{_, _, _, [file: expanding]}, {_, _, _, info} | _])
932936
when expanding in [~c"expanding macro", ~c"expanding struct"] do
933-
get_line_span_from_stacktrace_info(info, file)
937+
get_snippet_info_from_stacktrace_info(info, file)
934938
end
935939

936-
defp get_line_span(file, _reason, [{_, _, _, info} | _]) do
937-
get_line_span_from_stacktrace_info(info, file)
940+
defp get_snippet_info(file, _reason, [{_, _, _, info} | _]) do
941+
get_snippet_info_from_stacktrace_info(info, file)
938942
end
939943

940-
defp get_line_span(_, _, _) do
941-
{nil, nil}
944+
defp get_snippet_info(_, _, _) do
945+
{nil, nil, nil}
942946
end
943947

944-
defp get_line_span_from_stacktrace_info(info, file) do
948+
defp get_snippet_info_from_stacktrace_info(info, file) do
945949
if Keyword.get(info, :file) == to_charlist(Path.relative_to_cwd(file)) do
946-
{Keyword.get(info, :line), nil}
950+
{nil, Keyword.get(info, :line), nil}
947951
else
948-
{nil, nil}
952+
{nil, nil, nil}
949953
end
950954
end
951955
end

lib/elixir/lib/module/parallel_checker.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ defmodule Module.ParallelChecker do
324324
defp to_diagnostic(message, {file, position, mfa}) when is_list(position) do
325325
%{
326326
severity: :warning,
327+
source: file,
327328
file: file,
328329
position: position_to_tuple(position),
329330
message: IO.iodata_to_binary(message),

lib/elixir/src/elixir_errors.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ emit_diagnostic(Severity, Position, File, Message, Stacktrace, Options) ->
106106

107107
Diagnostic = #{
108108
severity => Severity,
109+
source => File,
109110
file => File,
110111
position => Position,
111112
message => unicode:characters_to_binary(Message),

lib/elixir/test/elixir/code_test.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ defmodule CodeTest do
252252
message: "undefined variable \"x\"",
253253
position: 1,
254254
file: "nofile",
255+
source: "nofile",
255256
stacktrace: [],
256257
severity: :error
257258
}

lib/elixir/test/elixir/kernel/parallel_compiler_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,24 @@ defmodule Kernel.ParallelCompilerTest do
480480
end)
481481
end
482482

483+
test "gets correct file+line+column number for SyntaxError" do
484+
File.mkdir_p!(tmp_path())
485+
486+
[fixture] =
487+
write_tmp("error",
488+
error: """
489+
raise SyntaxError, file: "foo/bar.ex", line: 3, column: 10
490+
"""
491+
)
492+
493+
file = Path.absname("foo/bar.ex")
494+
495+
capture_io(:stderr, fn ->
496+
assert {:error, [%{file: ^file, source: ^fixture, position: {3, 10}}], _} =
497+
Kernel.ParallelCompiler.compile([fixture], return_diagnostics: true)
498+
end)
499+
end
500+
483501
test "gets proper beam destinations from dynamic modules" do
484502
fixtures =
485503
write_tmp(

lib/mix/lib/mix/compilers/elixir.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,9 +724,11 @@ defmodule Mix.Compilers.Elixir do
724724
)} <- sources,
725725
file = Path.absname(source),
726726
{position, message, span} <- compile_warnings ++ runtime_warnings do
727+
# TODO: Store the whole diagnostic
727728
diagnostic = %Mix.Task.Compiler.Diagnostic{
728729
severity: :warning,
729730
file: file,
731+
source: file,
730732
position: position,
731733
message: message,
732734
compiler_name: "Elixir",
@@ -774,11 +776,13 @@ defmodule Mix.Compilers.Elixir do
774776
message: message,
775777
severity: severity,
776778
stacktrace: stacktrace,
777-
span: span
779+
span: span,
780+
source: source
778781
} = diagnostic
779782
) do
780783
%Mix.Task.Compiler.Diagnostic{
781784
file: file,
785+
source: source,
782786
position: position,
783787
message: message,
784788
severity: severity,

lib/mix/lib/mix/compilers/erlang.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,11 @@ defmodule Mix.Compilers.Erlang do
288288
defp to_diagnostics(warnings_or_errors, severity) do
289289
for {file, issues} <- warnings_or_errors,
290290
{location, module, data} <- issues do
291+
file = Path.absname(file)
292+
291293
%Mix.Task.Compiler.Diagnostic{
292-
file: Path.absname(file),
294+
file: file,
295+
source: file,
293296
position: location_normalize(location),
294297
message: to_string(module.format_error(data)),
295298
severity: severity,

lib/mix/lib/mix/task.compiler.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,24 @@ defmodule Mix.Task.Compiler do
3232
@moduledoc """
3333
Diagnostic information such as a warning or compilation error.
3434
35+
The file and position relate to where the diagnostic should be shown.
3536
If there is a file and position, then the diagnostic is precise
3637
and you can use the given file and position for generating snippets,
3738
IDEs annotations, and so on. An optional span is available with
3839
the line and column the diagnostic ends.
3940
4041
Otherwise, a stacktrace may be given, which you can place your own
4142
heuristics to provide better reporting.
43+
44+
The source field points to the source file the compiler tracked
45+
the error to. For example, a file `lib/foo.ex` may embed `.eex`
46+
templates from `lib/foo/bar.eex`. A syntax error on the EEx template
47+
will point to file `lib/foo/bar.eex` but the source is `lib/foo.ex`.
4248
"""
4349

4450
@type t :: %__MODULE__{
4551
file: Path.t() | nil,
52+
source: Path.t() | nil,
4653
severity: severity,
4754
message: IO.chardata(),
4855
position: Code.position(),
@@ -72,6 +79,7 @@ defmodule Mix.Task.Compiler do
7279
@enforce_keys [:file, :severity, :message, :position, :compiler_name]
7380
defstruct [
7481
:file,
82+
:source,
7583
:severity,
7684
:message,
7785
:position,

lib/mix/test/mix/tasks/compile.elixir_test.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
14431443

14441444
assert %Diagnostic{
14451445
file: ^file,
1446+
source: ^file,
14461447
severity: :warning,
14471448
position: {2, 13},
14481449
compiler_name: "Elixir",
@@ -1457,6 +1458,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
14571458

14581459
assert %Diagnostic{
14591460
file: ^file,
1461+
source: ^file,
14601462
severity: :warning,
14611463
position: {2, 13},
14621464
compiler_name: "Elixir",
@@ -1518,6 +1520,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
15181520

15191521
assert %Diagnostic{
15201522
file: ^file,
1523+
source: ^file,
15211524
severity: :error,
15221525
position: {2, 20},
15231526
message: "** (SyntaxError) invalid syntax found on lib/a.ex:2:" <> _,
@@ -1552,6 +1555,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
15521555

15531556
assert %Diagnostic{
15541557
file: ^file,
1558+
source: ^file,
15551559
severity: :error,
15561560
position: 2,
15571561
message: "** (KeyError) key :invalid_key not found" <> _,

0 commit comments

Comments
 (0)