Skip to content

Commit 184c771

Browse files
committed
Include previous clause line on default errors/warnings, closes #14804
1 parent 2b57a97 commit 184c771

File tree

2 files changed

+30
-17
lines changed

2 files changed

+30
-17
lines changed

lib/elixir/src/elixir_def.erl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ store_definition(CheckClauses, Kind, Meta, Name, Arity, File, Module, Defaults,
298298
case ets:lookup(Set, {def, Tuple}) of
299299
[{_, StoredKind, StoredMeta, StoredFile, StoredCheck, {StoredDefaults, LastHasBody, LastDefaults}}] ->
300300
check_valid_kind(Meta, File, Name, Arity, Kind, StoredKind, StoredFile, StoredMeta),
301-
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredDefaults, LastDefaults, HasBody, LastHasBody),
301+
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredMeta, StoredDefaults, LastDefaults, HasBody, LastHasBody),
302302
(CheckAll and (StoredCheck == all)) andalso
303303
check_valid_clause(Meta, File, Name, Arity, Kind, Set, StoredMeta, StoredFile, Clauses),
304304

@@ -380,17 +380,17 @@ check_valid_clause(Meta, File, Name, Arity, Kind, Set, StoredMeta, StoredFile, C
380380
end.
381381

382382
% Clause with defaults after clause with defaults
383-
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredDefaults, _, _, _)
383+
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredMeta, StoredDefaults, _, _, _)
384384
when Defaults > 0, StoredDefaults > 0 ->
385-
elixir_errors:file_error(Meta, File, ?MODULE, {duplicate_defaults, {Kind, Name, Arity}});
385+
elixir_errors:file_error(Meta, File, ?MODULE, {duplicate_defaults, {Kind, Name, Arity, StoredMeta}});
386386
% Clause with defaults after clause without defaults
387-
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, 0, _, _, _) when Defaults > 0 ->
388-
elixir_errors:file_warn(Meta, File, ?MODULE, {mixed_defaults, {Kind, Name, Arity}});
387+
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredMeta, 0, _, _, _) when Defaults > 0 ->
388+
elixir_errors:file_warn(Meta, File, ?MODULE, {mixed_defaults, {Kind, Name, Arity, StoredMeta}});
389389
% Clause without defaults directly after clause with defaults (bodiless does not count)
390-
check_valid_defaults(Meta, File, Name, Arity, Kind, 0, _, LastDefaults, true, true) when LastDefaults > 0 ->
391-
elixir_errors:file_warn(Meta, File, ?MODULE, {mixed_defaults, {Kind, Name, Arity}});
390+
check_valid_defaults(Meta, File, Name, Arity, Kind, 0, StoredMeta, _, LastDefaults, true, true) when LastDefaults > 0 ->
391+
elixir_errors:file_warn(Meta, File, ?MODULE, {mixed_defaults, {Kind, Name, Arity, StoredMeta}});
392392
% Clause without defaults
393-
check_valid_defaults(_Meta, _File, _Name, _Arity, _Kind, 0, _StoredDefaults, _LastDefaults, _HasBody, _LastHasBody) ->
393+
check_valid_defaults(_Meta, _File, _Name, _Arity, _Kind, 0, _StoredMeta, _StoredDefaults, _LastDefaults, _HasBody, _LastHasBody) ->
394394
ok.
395395

396396
check_args_for_function_head(Meta, Args, E) ->
@@ -445,7 +445,7 @@ format_error({defs_with_defaults, Kind, Name, Arity, A}) when Arity < A ->
445445
io_lib:format("~ts ~ts/~B conflicts with defaults from ~ts/~B",
446446
[Kind, Name, Arity, Name, A]);
447447

448-
format_error({duplicate_defaults, {Kind, Name, Arity}}) ->
448+
format_error({duplicate_defaults, {Kind, Name, Arity, StoredMeta}}) ->
449449
io_lib:format(
450450
"~ts ~ts/~B defines defaults multiple times. "
451451
"Elixir allows defaults to be declared once per definition. Instead of:\n"
@@ -457,10 +457,11 @@ format_error({duplicate_defaults, {Kind, Name, Arity}}) ->
457457
"\n"
458458
" def foo(a, b \\\\ :default)\n"
459459
" def foo(:first_clause, b) do ... end\n"
460-
" def foo(:second_clause, b) do ... end\n",
461-
[Kind, Name, Arity]);
460+
" def foo(:second_clause, b) do ... end\n"
461+
"~ts",
462+
[Kind, Name, Arity, maybe_stored_meta_line(StoredMeta)]);
462463

463-
format_error({mixed_defaults, {Kind, Name, Arity}}) ->
464+
format_error({mixed_defaults, {Kind, Name, Arity, StoredMeta}}) ->
464465
io_lib:format(
465466
"~ts ~ts/~B has multiple clauses and also declares default values. "
466467
"In such cases, the default values should be defined in a header. Instead of:\n"
@@ -472,8 +473,9 @@ format_error({mixed_defaults, {Kind, Name, Arity}}) ->
472473
"\n"
473474
" def foo(a, b \\\\ :default)\n"
474475
" def foo(:first_clause, b) do ... end\n"
475-
" def foo(:second_clause, b) do ... end\n",
476-
[Kind, Name, Arity]);
476+
" def foo(:second_clause, b) do ... end\n"
477+
"~ts",
478+
[Kind, Name, Arity, maybe_stored_meta_line(StoredMeta)]);
477479

478480
format_error({ungrouped_name, {Kind, Name, Arity, OrigLine, OrigFile}}) ->
479481
io_lib:format("clauses with the same name should be grouped together, \"~ts ~ts/~B\" was previously defined (~ts:~B)",
@@ -525,3 +527,10 @@ format_error({module_info, Kind, Arity}) ->
525527
format_error({is_record, Kind}) ->
526528
io_lib:format("cannot define ~ts is_record/2 due to compatibility "
527529
"with the Erlang compiler (it is a known limitation)", [Kind]).
530+
531+
maybe_stored_meta_line(StoredMeta) ->
532+
case lists:keyfind(line, 1, StoredMeta) of
533+
{line, Line} when Line > 0 ->
534+
"\nthe previous clause is defined on line " ++ integer_to_list(Line) ++ "\n";
535+
_ -> ""
536+
end.

lib/elixir/test/elixir/kernel/warning_test.exs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,7 @@ defmodule Kernel.WarningTest do
11331133
message = "def hello/1 has multiple clauses and also declares default values"
11341134

11351135
assert_warn_eval(
1136-
["nofile:3:7\n", message],
1136+
["nofile:3:7\n", message, "the previous clause is defined on line 2"],
11371137
~S"""
11381138
defmodule Sample1 do
11391139
def hello(arg), do: arg
@@ -1143,7 +1143,7 @@ defmodule Kernel.WarningTest do
11431143
)
11441144

11451145
assert_warn_eval(
1146-
["nofile:3:7\n", message],
1146+
["nofile:3:7\n", message, "the previous clause is defined on line 2"],
11471147
~S"""
11481148
defmodule Sample2 do
11491149
def hello(_arg)
@@ -1157,7 +1157,11 @@ defmodule Kernel.WarningTest do
11571157

11581158
test "clauses with default should use header" do
11591159
assert_warn_eval(
1160-
["nofile:3:7\n", "def hello/1 has multiple clauses and also declares default values"],
1160+
[
1161+
"nofile:3:7\n",
1162+
"def hello/1 has multiple clauses and also declares default values",
1163+
"the previous clause is defined on line 2"
1164+
],
11611165
~S"""
11621166
defmodule Sample do
11631167
def hello(arg \\ 0), do: arg

0 commit comments

Comments
 (0)