summaryrefslogtreecommitdiff
path: root/lib/compiler
diff options
context:
space:
mode:
authorHans Bolinder <hasse@erlang.org>2021-02-01 07:17:32 +0100
committerHans Bolinder <hasse@erlang.org>2021-02-01 07:17:32 +0100
commitee6a69bcef0dfdafe07906b939052d24e25be9c3 (patch)
treea778d0648b2b9fbbb3bc1eba374ce0d25a29a1e3 /lib/compiler
parent2ac9e0744e867f791662a5f279e91f6a7bf67199 (diff)
parent370a2f1c940d305818eb6e415e874741382c22d9 (diff)
downloaderlang-ee6a69bcef0dfdafe07906b939052d24e25be9c3.tar.gz
Merge branch 'richcarl/columns/PR-2664/OTP-16824'
* richcarl/columns/PR-2664/OTP-16824: (56 commits) Update primary bootstrap erl_lint: Give a better column position for format warnings erl_lint: Correct column number for unsized binary not at end Include column numbers in compiler warnings dialyzer: Improve column numbers in warnings stdlib: Improve locations of annotations of abstract code stdlib: Improve error locations in module erl_lint stdlib: Improve error locations in module epp stdlib: Improve error locations in module erl_parse stdlib: Fix handling of annotations in erl_expand_records erl_docgen: Correct handling of annotation of abstract code syntax_tools: Generalize start line to be location syntax_tools: Correct handling of annotations of abstract code edoc: Correct handling of annotations of abstract code tools: Substitute Anno for Line in xref_reader tools: Substitute Anno for Line in cover stdlib: Substitute Anno for Line in epp stdlib: Substitute Anno for Line in erl_eval stdlib: Substitute Anno for Line in erl_pp stdlib: Substitute Anno for Line in eval_bits ...
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/doc/src/compile.xml28
-rw-r--r--lib/compiler/src/beam_kernel_to_ssa.erl6
-rw-r--r--lib/compiler/src/beam_validator.erl2
-rw-r--r--lib/compiler/src/compile.erl66
-rw-r--r--lib/compiler/src/core_pp.erl4
-rw-r--r--lib/compiler/src/sys_core_fold.erl102
-rw-r--r--lib/compiler/src/sys_pre_attributes.erl16
-rw-r--r--lib/compiler/src/v3_core.erl11
-rw-r--r--lib/compiler/src/v3_kernel.erl16
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl2
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl6
-rw-r--r--lib/compiler/test/compile_SUITE.erl21
-rw-r--r--lib/compiler/test/error_SUITE.erl151
-rw-r--r--lib/compiler/test/warnings_SUITE.erl539
14 files changed, 655 insertions, 315 deletions
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 8329ed867c..467f01b9d8 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -355,6 +355,20 @@ module.beam: module.erl \
since R13B04.</p>
</item>
+ <tag><c>error_location</c></tag>
+ <item>
+ <p>If the value of this flag is <c>line</c>, the location
+ <seeerl marker="#error_information"><c>ErrorLocation</c></seeerl>
+ of warnings and errors is a line number. If
+ the value is <c>column</c>, <c>ErrorLocation</c> includes
+ both a line number and a column number.
+ Default is <c>column</c>. This option is supported
+ since Erlang/OTP 24.0.</p>
+ <p>If the value of this flag is <c>column</c>,
+ <seeerl marker="#debug_info">debug information</seeerl>
+ includes column information.</p>
+ </item>
+
<tag><c>return</c></tag>
<item>
<p>A short form for both <c>return_errors</c> and
@@ -783,7 +797,7 @@ module.beam: module.erl \
The filename is included here, as the compiler uses the
Erlang pre-processor <c>epp</c>, which allows the code to be
included in other files. It is therefore important to know to
- <em>which</em> file the line number of an error or a warning refers.
+ <em>which</em> file the location of an error or a warning refers.
</p>
</desc>
</func>
@@ -810,7 +824,7 @@ module.beam: module.erl \
<v>ModuleName = module()</v>
<v>BinaryOrCode = binary() | term()</v>
<v>ErrRet = error | {error,Errors,Warnings}</v>
- <v>Warnings = Errors = [{<seetype marker="kernel:file#filename">file:filename()</seetype>, [{<seetype marker="stdlib:erl_anno#line">erl_anno:line()</seetype> | 'none', module(), term()}]}]</v>
+ <v>Warnings = Errors = [{<seetype marker="kernel:file#filename">file:filename()</seetype>, [{<seetype marker="stdlib:erl_anno#location">erl_anno:location()</seetype> | 'none', module(), term()}]}]</v>
</type>
<desc>
<p>Analogous to <c>file/1</c>, but takes a list of forms (in
@@ -1003,6 +1017,10 @@ pi() -> 3.1416.
<p>Parse transformations are used when a programmer wants to use
Erlang syntax but with different semantics. The original Erlang
code is then transformed into other Erlang code.</p>
+
+ <p>See <seeerl marker="stdlib:erl_id_trans">erl_id_trans(3)</seeerl>
+ for an example and an explanation of the function
+ <c>parse_transform_info/0</c>.</p>
</section>
<section>
@@ -1013,10 +1031,10 @@ pi() -> 3.1416.
<c>ErrorInfo</c> structure, which is returned from all I/O modules.
It has the following format:</p>
<code>
-{ErrorLine, Module, ErrorDescriptor}</code>
+{ErrorLocation, Module, ErrorDescriptor}</code>
- <p><c>ErrorLine</c> is the atom <c>none</c> if the error does
- not correspond to a specific line, for example, if the source file does
+ <p><c>ErrorLocation</c> is the atom <c>none</c> if the error does
+ not correspond to a specific location, for example, if the source file does
not exist.</p>
<p>A string describing the error is obtained with the following
diff --git a/lib/compiler/src/beam_kernel_to_ssa.erl b/lib/compiler/src/beam_kernel_to_ssa.erl
index 7ba2d925e4..0c79f00d1f 100644
--- a/lib/compiler/src/beam_kernel_to_ssa.erl
+++ b/lib/compiler/src/beam_kernel_to_ssa.erl
@@ -1276,6 +1276,9 @@ new_label(#cg{lcount=Next}=St) ->
line_anno([Line,{file,Name}]) when is_integer(Line) ->
line_anno_1(Name, Line);
+line_anno([{Line,Column},{file,Name}]) when is_integer(Line),
+ is_integer(Column) ->
+ line_anno_1(Name, Line);
line_anno([_|_]=A) ->
{Name,Line} = find_loc(A, no_file, 0),
line_anno_1(Name, Line);
@@ -1292,6 +1295,9 @@ line_anno_1(Name, Line) ->
find_loc([Line|T], File, _) when is_integer(Line) ->
find_loc(T, File, Line);
+find_loc([{Line, Column}|T], File, _) when is_integer(Line),
+ is_integer(Column) ->
+ find_loc(T, File, Line);
find_loc([{file,File}|T], _, Line) ->
find_loc(T, File, Line);
find_loc([_|T], File, Line) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 233167b536..794ba7874e 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -57,7 +57,7 @@ validate({Mod,Exp,Attr,Fs,Lc}, Level) when is_atom(Mod),
[] ->
ok;
Es0 ->
- Es = [{?MODULE,E} || E <- Es0],
+ Es = [{1,?MODULE,E} || E <- Es0],
{error,[{atom_to_list(Mod),Es}]}
end.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 5ccaaf1714..4fec417c6e 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -375,7 +375,13 @@ internal({forms,Forms}, Opts0) ->
Source = proplists:get_value(source, Opts0, ""),
Opts1 = proplists:delete(source, Opts0),
Compile = build_compile(Opts1),
- internal_comp(Ps, Forms, Source, "", Compile);
+ NewForms = case with_columns(Opts0) of
+ true ->
+ Forms;
+ false ->
+ strip_columns(Forms)
+ end,
+ internal_comp(Ps, NewForms, Source, "", Compile);
internal({file,File}, Opts) ->
{Ext,Ps} = passes(file, Opts),
Compile = build_compile(Opts),
@@ -1007,18 +1013,32 @@ do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) ->
true -> filename:basename(SourceName0);
false -> SourceName0
end,
+ StartLocation = case with_columns(Opts) of
+ true ->
+ {1,1};
+ false ->
+ 1
+ end,
R = epp:parse_file(File,
[{includes,[".",Dir|inc_paths(Opts)]},
{source_name, SourceName},
{macros,pre_defs(Opts)},
{default_encoding,DefEncoding},
+ {location,StartLocation},
extra]),
case R of
{ok,Forms,Extra} ->
Encoding = proplists:get_value(encoding, Extra),
case find_invalid_unicode(Forms, File) of
none ->
- {ok,Forms,St#compile{encoding=Encoding}};
+ Forms1 =
+ case with_columns(Opts ++ compile_options(Forms)) of
+ true ->
+ Forms;
+ false ->
+ strip_columns(Forms)
+ end,
+ {ok,Forms1,St#compile{encoding=Encoding}};
{invalid_unicode,_,_}=Ret ->
case Encoding of
none ->
@@ -1032,6 +1052,9 @@ do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) ->
{error,St#compile{errors=St#compile.errors ++ Es}}
end.
+with_columns(Opts) ->
+ proplists:get_value(error_location, Opts, column) =:= column.
+
find_invalid_unicode([H|T], File0) ->
case H of
{attribute,_,file,{File,_}} ->
@@ -1135,10 +1158,12 @@ foldl_transform([T|Ts], Code0, St) ->
Es = [{St#compile.ifile,[{none,compile,
{parse_transform,T,R}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}};
- {warning, Forms, Ws} ->
+ {warning, Forms0, Ws} ->
+ Forms = maybe_strip_columns(Forms0, T),
foldl_transform(Ts, Forms,
St#compile{warnings=St#compile.warnings ++ Ws});
- Forms ->
+ Forms0 ->
+ Forms = maybe_strip_columns(Forms0, T),
foldl_transform(Ts, Forms, St)
end;
false ->
@@ -1148,6 +1173,27 @@ foldl_transform([T|Ts], Code0, St) ->
end;
foldl_transform([], Code, St) -> {ok,Code,St}.
+%%% It is possible--although unlikely--that parse transforms add
+%%% columns to the abstract code, why this function is called for
+%%% every parse transform.
+maybe_strip_columns(Code, T) ->
+ case erlang:function_exported(T, parse_transform_info, 0) of
+ true ->
+ Info = T:parse_transform_info(),
+ case maps:get(error_location, Info, column) of
+ line ->
+ strip_columns(Code);
+ _ ->
+ Code
+ end;
+ false ->
+ Code
+ end.
+
+strip_columns(Code) ->
+ F = fun(A) -> erl_anno:set_location(erl_anno:line(A), A) end,
+ [erl_parse:map_anno(F, Form) || Form <- Code].
+
get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts].
core_transforms(Code, St) ->
@@ -1729,18 +1775,13 @@ format_message(F, P, [{none,Mod,E}|Es]) ->
M = {none,io_lib:format("~ts: ~s~ts\n", [F,P,Mod:format_error(E)])},
[M|format_message(F, P, Es)];
format_message(F, P, [{{Line,Column}=Loc,Mod,E}|Es]) ->
- M = {{F,Loc},io_lib:format("~ts:~w:~w ~s~ts\n",
+ M = {{F,Loc},io_lib:format("~ts:~w:~w: ~s~ts\n",
[F,Line,Column,P,Mod:format_error(E)])},
[M|format_message(F, P, Es)];
format_message(F, P, [{Line,Mod,E}|Es]) ->
M = {{F,{Line,0}},io_lib:format("~ts:~w: ~s~ts\n",
[F,Line,P,Mod:format_error(E)])},
[M|format_message(F, P, Es)];
-format_message(F, P, [{Mod,E}|Es]) ->
- %% Not documented and not expected to be used any more, but
- %% keep a while just in case.
- M = {none,io_lib:format("~ts: ~s~ts\n", [F,P,Mod:format_error(E)])},
- [M|format_message(F, P, Es)];
format_message(_, _, []) -> [].
%% list_errors(File, ErrorDescriptors) -> ok
@@ -1754,11 +1795,6 @@ list_errors(F, [{{Line,Column},Mod,E}|Es]) ->
list_errors(F, [{Line,Mod,E}|Es]) ->
io:fwrite("~ts:~w: ~ts\n", [F,Line,Mod:format_error(E)]),
list_errors(F, Es);
-list_errors(F, [{Mod,E}|Es]) ->
- %% Not documented and not expected to be used any more, but
- %% keep a while just in case.
- io:fwrite("~ts: ~ts\n", [F,Mod:format_error(E)]),
- list_errors(F, Es);
list_errors(_F, []) -> ok.
%% erlfile(Dir, Base) -> ErlFile
diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl
index 19fa11235c..ab92f3dfce 100644
--- a/lib/compiler/src/core_pp.erl
+++ b/lib/compiler/src/core_pp.erl
@@ -101,6 +101,8 @@ format_anno_list([H], Ctxt) ->
strip_line([A | As]) when is_integer(A) ->
strip_line(As);
+strip_line([{A,C} | As]) when is_integer(A), is_integer(C) ->
+ strip_line(As);
strip_line([{file,_File} | As]) ->
strip_line(As);
strip_line([A | As]) ->
@@ -110,6 +112,8 @@ strip_line([]) ->
get_line([L | _As]) when is_integer(L) ->
L;
+get_line([{L, _Column} | _As]) when is_integer(L) ->
+ L;
get_line([_ | As]) ->
get_line(As);
get_line([]) ->
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 704bddc2ac..287280c18f 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -97,7 +97,8 @@
-record(sub, {v=[], %Variable substitutions
s=sets:new([{version, 2}]) :: sets:set(), %Variables in scope
t=#{} :: map(), %Types
- in_guard=false}). %In guard or not.
+ in_guard=false, %In guard or not.
+ top=true}). %Not inside a term.
-spec module(cerl:c_module(), [compile:option()]) ->
{'ok', cerl:c_module(), [_]}.
@@ -176,7 +177,7 @@ expr(#c_var{}=V, Ctxt, Sub) ->
effect -> void();
value -> sub_get_var(V, Sub)
end;
-expr(#c_literal{val=Val}=L, Ctxt, _Sub) ->
+expr(#c_literal{val=Val}=L, Ctxt, Sub) ->
case Ctxt of
effect ->
case Val of
@@ -188,35 +189,36 @@ expr(#c_literal{val=Val}=L, Ctxt, _Sub) ->
void();
_ ->
%% Warn and replace with void().
- add_warning(L, useless_building),
+ warn_useless_building(L, Sub),
void()
end;
value -> L
end;
expr(#c_cons{anno=Anno,hd=H0,tl=T0}=Cons, Ctxt, Sub) ->
- H1 = expr(H0, Ctxt, Sub),
- T1 = expr(T0, Ctxt, Sub),
+ DeeperSub = descend(Cons, Sub),
+ H1 = expr(H0, Ctxt, DeeperSub),
+ T1 = expr(T0, Ctxt, DeeperSub),
case Ctxt of
effect ->
- add_warning(Cons, useless_building),
+ warn_useless_building(Cons, Sub),
make_effect_seq([H1,T1], Sub);
value ->
ann_c_cons(Anno, H1, T1)
end;
expr(#c_tuple{anno=Anno,es=Es0}=Tuple, Ctxt, Sub) ->
- Es = expr_list(Es0, Ctxt, Sub),
+ Es = expr_list(Es0, Ctxt, descend(Tuple, Sub)),
case Ctxt of
effect ->
- add_warning(Tuple, useless_building),
+ warn_useless_building(Tuple, Sub),
make_effect_seq(Es, Sub);
value ->
ann_c_tuple(Anno, Es)
end;
expr(#c_map{anno=Anno,arg=V0,es=Es0}=Map, Ctxt, Sub) ->
- Es = pair_list(Es0, Ctxt, Sub),
+ Es = pair_list(Es0, Ctxt, descend(Map, Sub)),
case Ctxt of
effect ->
- add_warning(Map, useless_building),
+ warn_useless_building(Map, Sub),
make_effect_seq(Es, Sub);
value ->
V = expr(V0, Ctxt, Sub),
@@ -226,15 +228,15 @@ expr(#c_binary{segments=Ss}=Bin0, Ctxt, Sub) ->
%% Warn for useless building, but always build the binary
%% anyway to preserve a possible exception.
case Ctxt of
- effect -> add_warning(Bin0, useless_building);
+ effect -> warn_useless_building(Bin0, Sub);
value -> ok
end,
Bin1 = Bin0#c_binary{segments=bitstr_list(Ss, Sub)},
Bin = bin_un_utf(Bin1),
eval_binary(Bin);
-expr(#c_fun{}=Fun, effect, _) ->
+expr(#c_fun{}=Fun, effect, Sub) ->
%% A fun is created, but not used. Warn, and replace with the void value.
- add_warning(Fun, useless_building),
+ warn_useless_building(Fun, Sub),
void();
expr(#c_fun{vars=Vs0,body=B0}=Fun, Ctxt0, Sub0) ->
{Vs1,Sub1} = var_list(Vs0, Sub0),
@@ -2712,6 +2714,63 @@ copy_type(_, _, Tdb) -> Tdb.
void() -> #c_literal{val=ok}.
+%%%
+%%% Handling of the `useless_building` warning (building a term that
+%%% is never used).
+%%%
+%%% Consider this code fragment:
+%%%
+%%% [ {ok,Term} ],
+%%% ok
+%%%
+%%% The list that is ignored contains a tuple that is also ignored.
+%%% While optimizing this code fragment, two warnings for useless
+%%% building will be generated: one for the list and one for the tuple
+%%% inside. Before the introduction of column numbers, those two warnings
+%%% would be coalesced to one becuase they had the same line number.
+%%%
+%%% With column numbers, we will need a more sophisticated solution to
+%%% avoid emitting annoying duplicate warnings.
+%%%
+%%% Note that if two separate terms are being built on the same line, we
+%%% do expect to get two warnings:
+%%%
+%%% [ {ok,Term} ], [ {error,BadTerm} ], ok
+%%% ^ ^
+%%%
+%%% (The carets mark the expected columns for the warnings.)
+%%%
+%%% To handle those requirements, we will use the #sub{} record to keep
+%%% track of whether we are at the top level or have descended into
+%%% a sub expression.
+%%%
+
+%% Note in the Sub record that we have are no longer at the top level.
+descend(_Core, #sub{top=false}=Sub) ->
+ Sub;
+descend(Core, #sub{top=true}=Sub) ->
+ case should_suppress_warning(Core) of
+ true ->
+ %% In a list comprehension being ignored such as:
+ %%
+ %% [{error,Z} || Z <- List], ok
+ %%
+ %% the warning for ignoring the cons cell should be
+ %% suppressed, but there should still be a warning for
+ %% ignoring the {error,Z} tuple. Therefore, pretend that
+ %% we are still at the top level.
+ Sub;
+ false ->
+ %% No longer at top level. Warnings for useless building
+ %% should now be suppressed.
+ Sub#sub{top=false}
+ end.
+
+warn_useless_building(Core, #sub{top=Top}) ->
+ case Top of
+ true -> add_warning(Core, useless_building);
+ false -> ok
+ end.
%%%
%%% Handling of warnings.
@@ -2719,29 +2778,38 @@ void() -> #c_literal{val=ok}.
init_warnings() ->
put({?MODULE,warnings}, []).
-
add_warning(Core, Term) ->
case should_suppress_warning(Core) of
true ->
ok;
false ->
Anno = cerl:get_ann(Core),
- Line = get_line(Anno),
+ Location = get_location(Anno),
File = get_file(Anno),
Key = {?MODULE,warnings},
case get(Key) of
- [{File,[{Line,?MODULE,Term}]}|_] ->
+ [{File,[{Location,?MODULE,Term}]}|_] ->
ok; %We already have
%an identical warning.
Ws ->
- put(Key, [{File,[{Line,?MODULE,Term}]}|Ws])
+ put(Key, [{File,[{Location,?MODULE,Term}]}|Ws])
end
end.
get_line([Line|_]) when is_integer(Line) -> Line;
+get_line([{Line, _Column} | _T]) when is_integer(Line) -> Line;
get_line([_|T]) -> get_line(T);
get_line([]) -> none.
+get_location([Line|_]) when is_integer(Line) ->
+ Line;
+get_location([{Line, Column} | _T]) when is_integer(Line), is_integer(Column) ->
+ {Line,Column};
+get_location([_|T]) ->
+ get_location(T);
+get_location([]) ->
+ none.
+
get_file([{file,File}|_]) -> File;
get_file([_|T]) -> get_file(T);
get_file([]) -> "no_file". % should not happen
diff --git a/lib/compiler/src/sys_pre_attributes.erl b/lib/compiler/src/sys_pre_attributes.erl
index 67adae5acf..1b0fcd46af 100644
--- a/lib/compiler/src/sys_pre_attributes.erl
+++ b/lib/compiler/src/sys_pre_attributes.erl
@@ -114,7 +114,7 @@ pre_transform(S) ->
pre_transform([H | T], Acc, S) ->
case H of
- {attribute, Line, Name, Val} ->
+ {attribute, Anno, Name, Val} ->
case lists:keyfind(Name, 2, S#state.pre_ops) of
false ->
pre_transform(T, [H | Acc], S);
@@ -123,7 +123,7 @@ pre_transform([H | T], Acc, S) ->
report_warning("Replace attribute ~p: ~p -> ~p~n",
[Name, Val, NewVal],
S),
- New = {attribute, Line, Name, NewVal},
+ New = {attribute, Anno, Name, NewVal},
Pre = lists:keydelete(Name, 2, S#state.pre_ops),
Post = lists:keydelete(Name, 2, S#state.post_ops),
S2 = S#state{pre_ops = Pre, post_ops = Post},
@@ -158,9 +158,9 @@ post_transform(S) ->
post_transform([H | T], Acc, S) ->
case H of
- {attribute, Line, module, _Val} = Attribute ->
+ {attribute, Anno, module, _Val} = Attribute ->
Acc2 = lists:reverse([Attribute | Acc]),
- Forms = Acc2 ++ attrs(S#state.post_ops, Line, S) ++ T,
+ Forms = Acc2 ++ attrs(S#state.post_ops, Anno, S) ++ T,
S#state{forms = Forms, post_ops = []};
_Any ->
post_transform(T, [H | Acc], S)
@@ -168,12 +168,12 @@ post_transform([H | T], Acc, S) ->
post_transform([], Acc, S) ->
S#state{forms = lists:reverse(Acc)}.
-attrs([{replace, Name, NewVal} | T], Line, S) ->
+attrs([{replace, Name, NewVal} | T], Anno, S) ->
report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
- [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
-attrs([{insert, Name, NewVal} | T], Line, S) ->
+ [{attribute, Anno, Name, NewVal} | attrs(T, Anno, S)];
+attrs([{insert, Name, NewVal} | T], Anno, S) ->
report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
- [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
+ [{attribute, Anno, Name, NewVal} | attrs(T, Anno, S)];
attrs([], _, _) ->
[].
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index d4ee6053ab..1b9e961514 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -947,20 +947,21 @@ map_build_pairs_1([{Op0,L,K0,V0}|Es], Used0, St0) ->
{Pairs,Pre2,Used1,St3} = map_build_pairs_1(Es, Used0, St2),
As = lineno_anno(L, St3),
Op = map_op(Op0),
- {Used2,St4} = maybe_warn_repeated_keys(K, L, Used1, St3),
+ {Used2,St4} = maybe_warn_repeated_keys(K, K0, Used1, St3),
Pair = cerl:ann_c_map_pair(As, Op, K, V),
{[Pair|Pairs],Pre0++Pre1++Pre2,Used2,St4};
map_build_pairs_1([], Used, St) ->
{[],[],Used,St}.
-maybe_warn_repeated_keys(Ck,Line,Used,St) ->
+maybe_warn_repeated_keys(Ck, K0, Used, St) ->
case cerl:is_literal(Ck) of
false -> {Used,St};
true ->
K = cerl:concrete(Ck),
case sets:is_element(K,Used) of
true ->
- {Used, add_warning(Line, {map_key_repeated,K}, St)};
+ Location = element(2, K0),
+ {Used, add_warning(Location, {map_key_repeated,K}, St)};
false ->
{sets:add_element(K,Used), St}
end
@@ -3389,10 +3390,10 @@ full_anno(L, #core{wanted=true}=St) ->
lineno_anno(L, St).
lineno_anno(L, St) ->
- Line = erl_anno:line(L),
+ Location = erl_anno:location(L),
Generated = erl_anno:generated(L),
CompilerGenerated = [compiler_generated || Generated],
- [Line] ++ St#core.file ++ CompilerGenerated.
+ [Location] ++ St#core.file ++ CompilerGenerated.
get_lineno_anno(Ce) ->
case get_anno(Ce) of
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index eb04a2c916..40cf1c276c 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -338,7 +338,7 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
error ->
%% Invalid call (e.g. M:42/3). Issue a warning, and let
%% the generated code use the old explict apply.
- St = add_warning(get_line(A), bad_call, A, St0),
+ St = add_warning(get_location(A), bad_call, A, St0),
Call = #c_call{anno=A,
module=#c_literal{val=erlang},
name=#c_literal{val=apply},
@@ -1066,7 +1066,7 @@ maybe_add_warning(Ke, MatchAnno, St) ->
St;
false ->
Anno = get_kanno(Ke),
- Line = get_line(Anno),
+ Line = get_location(Anno),
MatchLine = get_line(MatchAnno),
Warn = case MatchLine of
none -> nomatch_shadow;
@@ -1074,8 +1074,18 @@ maybe_add_warning(Ke, MatchAnno, St) ->
end,
add_warning(Line, Warn, Anno, St)
end.
-
+
+get_location([Line|_]) when is_integer(Line) ->
+ Line;
+get_location([{Line, Column} | _T]) when is_integer(Line), is_integer(Column) ->
+ {Line,Column};
+get_location([_|T]) ->
+ get_location(T);
+get_location([]) ->
+ none.
+
get_line([Line|_]) when is_integer(Line) -> Line;
+get_line([{Line, _Column} | _T]) when is_integer(Line) -> Line;
get_line([_|T]) -> get_line(T);
get_line([]) -> none.
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index f7479e6b15..5a4e6fa788 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -57,6 +57,8 @@ format(Node, Ctxt) ->
format_1(Node, Ctxt);
[L,{file,_}] when is_integer(L) ->
format_1(Node, Ctxt);
+ [{L,C},{file,_}] when is_integer(L), is_integer(C) ->
+ format_1(Node, Ctxt);
List ->
format_anno(List, Ctxt, fun (Ctxt1) ->
format_1(Node, Ctxt1)
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 49731f9137..565a86be98 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -99,7 +99,7 @@ compiler_bug(Config) when is_list(Config) ->
%% the beam_validator module.
{error,
[{"compiler_bug",
- [{beam_validator,_}]}],
+ [{_Pos,beam_validator,_}]}],
[]} = compile:file(File, [from_asm,return_errors,time]),
ok.
@@ -892,7 +892,7 @@ do_val(Mod, Config) ->
case compile:file(File, [from_asm,no_postopt,return_errors]) of
{error,L,[]} ->
[{Base,Errors0}] = L,
- Errors = [E || {beam_validator,E} <- Errors0],
+ Errors = [E || {_Pos,beam_validator,E} <- Errors0],
_ = [io:put_chars(beam_validator:format_error(E)) ||
E <- Errors],
Errors;
@@ -903,7 +903,7 @@ do_val(Mod, Config) ->
beam_val(M) ->
Name = atom_to_list(element(1, M)),
{error,[{Name,Errors0}]} = beam_validator:validate(M, strong),
- Errors = [E || {beam_validator,E} <- Errors0],
+ Errors = [E || {_Pos,beam_validator,E} <- Errors0],
_ = [io:put_chars(beam_validator:format_error(E)) ||
E <- Errors],
Errors.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 290ed87016..6af2d9b2b5 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -1240,9 +1240,11 @@ do_warnings_1([{Name,Ws}|T], F) ->
end;
do_warnings_1([], _) -> ok.
-do_warnings_2([{Int,_,_}=W|T], Next, F) ->
- if
- is_integer(Int) ->
+do_warnings_2([{Pos,_,_}=W|T], Next, F) ->
+ case Pos of
+ Line when is_integer(Line) ->
+ do_warnings_2(T, Next, F);
+ {Line,Col} when is_integer(Line), is_integer(Col) ->
do_warnings_2(T, Next, F);
true ->
io:format("~s:\nMissing line number: ~p\n",
@@ -1502,14 +1504,17 @@ debug_info_attribute(DataDir, Name, Opts) ->
{ok,_,Bin} = compile:file(File, [binary | Opts]),
{ok, {_, Attrs}} = beam_lib:chunks(Bin, [debug_info]),
- [{debug_info,{debug_info_v1,erl_abstract_code,
- {[{attribute,1,file,{_,1}},
- {attribute,1,module,debug_info},
- {attribute,2,compile,[debug_info]},
- {eof,2}], _}}}] = Attrs,
+ [{debug_info,{debug_info_v1,erl_abstract_code, {Forms, _}}}] = Attrs,
+ [{attribute,{1,1},file,{_,1}},
+ {attribute,{1,2},module,debug_info},
+ {attribute,{2,2},compile,[debug_info]},
+ {eof,_}] = forms_to_terms(Forms),
ok.
+forms_to_terms(Forms) ->
+ [erl_parse:anno_to_term(Form) || Form <- Forms].
+
%%%
%%% Utilities.
%%%
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index 9436ad5d53..388ecce4f9 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -23,7 +23,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1,
- transforms/1,maps_warnings/1,bad_utf8/1]).
+ transforms/1,maps_warnings/1,bad_utf8/1,bad_decls/1]).
%% Used by transforms/1 test case.
-export([parse_transform/2]).
@@ -36,7 +36,7 @@ all() ->
groups() ->
[{p,test_lib:parallel(),
[head_mismatch_line,warnings_as_errors,bif_clashes,
- transforms,maps_warnings,bad_utf8]}].
+ transforms,maps_warnings,bad_utf8,bad_decls]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -64,7 +64,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[return_warnings],
{error,
- [{4, erl_lint,{call_to_redefined_old_bif,{length,1}}}], []} }],
+ [{{4,18}, erl_lint,{call_to_redefined_old_bif,{length,1}}}], []} }],
[] = run(Config, Ts),
Ts1 = [{bif_clashes2,
<<"
@@ -75,7 +75,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[return_warnings],
{error,
- [{3, erl_lint,{redefine_old_bif_import,{length,1}}}], []} }],
+ [{{3,16}, erl_lint,{redefine_old_bif_import,{length,1}}}], []} }],
[] = run(Config, Ts1),
Ts00 = [{bif_clashes3,
<<"
@@ -112,7 +112,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[return_warnings],
{warning,
- [{4, erl_lint,{call_to_redefined_bif,{binary_part,3}}}]} }],
+ [{{4,18}, erl_lint,{call_to_redefined_bif,{binary_part,3}}}]} }],
[] = run(Config, Ts000),
Ts111 = [{bif_clashes6,
<<"
@@ -123,7 +123,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[return_warnings],
{warning,
- [{3, erl_lint,{redefine_bif_import,{binary_part,3}}}]} }],
+ [{{3,16}, erl_lint,{redefine_bif_import,{binary_part,3}}}]} }],
[] = run(Config, Ts111),
Ts2 = [{bif_clashes7,
<<"
@@ -137,7 +137,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[],
{error,
- [{7,erl_lint,{define_import,{length,1}}}],
+ [{{7,15},erl_lint,{define_import,{length,1}}}],
[]} }],
[] = run2(Config, Ts2),
Ts3 = [{bif_clashes8,
@@ -151,7 +151,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[],
{error,
- [{4,erl_lint,{illegal_guard_local_call,{length,1}}}],
+ [{{4,25},erl_lint,{illegal_guard_local_call,{length,1}}}],
[]} }],
[] = run2(Config, Ts3),
Ts4 = [{bif_clashes9,
@@ -164,7 +164,7 @@ bif_clashes(Config) when is_list(Config) ->
">>,
[],
{error,
- [{5,erl_lint,{illegal_guard_local_call,{length,1}}}],
+ [{{5,25},erl_lint,{illegal_guard_local_call,{length,1}}}],
[]} }],
[] = run2(Config, Ts4),
@@ -176,7 +176,7 @@ bif_clashes(Config) when is_list(Config) ->
%% Tests that a head mismatch is reported on the correct line (OTP-2125).
head_mismatch_line(Config) when is_list(Config) ->
[E|_] = get_compilation_errors(Config, "head_mismatch_line"),
- {26, Mod, Reason} = E,
+ {{26,1}, Mod, Reason} = E,
Mod:format_error(Reason),
ok.
@@ -202,7 +202,7 @@ warnings_as_errors(Config) when is_list(Config) ->
[warnings_as_errors, export_all, {outdir, OutDir}],
{error,
[],
- [{3,erl_lint,{unused_var,'A'}}]} }],
+ [{{3,18},erl_lint,{unused_var,'A'}}]} }],
[] = run(Ts1, TestFile, write_beam),
false = filelib:is_regular(BeamFile),
@@ -214,7 +214,7 @@ warnings_as_errors(Config) when is_list(Config) ->
">>,
[return_warnings, export_all, {outdir, OutDir}],
{warning,
- [{3,erl_lint,{unused_var,'A'}}]} }],
+ [{{3,18},erl_lint,{unused_var,'A'}}]} }],
[] = run(Ts2, TestFile, write_beam),
true = filelib:is_regular(BeamFile),
@@ -270,7 +270,7 @@ maps_warnings(Config) when is_list(Config) ->
id(I) -> I.
">>,
[return],
- {error,[{3,erl_lint,{unbound_var,'K'}}],[]}}
+ {error,[{{3,15},erl_lint,{unbound_var,'K'}}],[]}}
],
[] = run2(Config, Ts1),
ok.
@@ -285,13 +285,102 @@ bad_utf8(Config) ->
t() -> \"",246,"\".
">>,
[],
- {error,[{2,epp,cannot_parse},
- {2,file_io_server,invalid_unicode}],
+ {error,[{{2,15},epp,cannot_parse},
+ {{2,15},file_io_server,invalid_unicode}],
[]}
}],
[] = run2(Config, Ts),
ok.
+bad_decls(Config) ->
+ Ts = [{bad_decls_1,
+ <<"\n-module({l}).
+ ">>,
+ [],
+ {error,[{{2,9},erl_parse,"bad " ++ ["module"] ++ " declaration"}],
+ []}
+ },
+ {bad_decls_2,
+ <<"\n-module(l, m).
+ ">>,
+ [],
+ {error,[{{2,12},erl_parse,"bad variable list"}],[]}
+ },
+ {bad_decls_3,
+ <<"\n-export([a/1], Y).
+ ">>,
+ [],
+ {error,[{{2,16},erl_parse,"bad " ++ ["export"] ++ " declaration"}],
+ []}
+ },
+ {bad_decls_4,
+ <<"\n-import([a/1], Y).
+ ">>,
+ [],
+ {error,[{{2,16},erl_parse,"bad " ++ ["import"] ++ " declaration"}],
+ []}
+ },
+ {bad_decls_5,
+ <<"\n-ugly({A,B}).
+ ">>,
+ [],
+ {error,[{{2,7},erl_parse,"bad attribute"}],[]}
+ },
+ {bad_decls_6,
+ <<"\n-ugly(a, b).
+ ">>,
+ [],
+ {error,[{{2,10},erl_parse,"bad attribute"}],[]}
+ },
+ {bad_decls_7,
+ <<"\n-export([A/1]).
+ ">>,
+ [],
+ {error,[{{2,10},erl_parse,"bad function name"}],[]}
+ },
+ {bad_decls_8,
+ <<"\n-export([a/a]).
+ ">>,
+ [],
+ {error,[{{2,12},erl_parse,"bad function arity"}],[]}
+ },
+ {bad_decls_9,
+ <<"\n-export([a/1, {3,4}]).
+ ">>,
+ [],
+ {error,[{{2,15},erl_parse,"bad Name/Arity"}],[]}
+ },
+ {bad_decls_10,
+ <<"\n-record(A, {{bad,a}}).
+ ">>,
+ [],
+ {error,[{{2,9},erl_parse,"bad " ++ ["record"] ++ " declaration"}],
+ []}
+ },
+ {bad_decls_11,
+ <<"\n-record(a, [a,b,c,d]).
+ ">>,
+ [],
+ {error,[{{2,12},erl_parse,"bad record declaration"}],[]}
+ },
+ {bad_decls_12,
+ <<"\n-record(a).
+ ">>,
+ [],
+ {error,[{{2,9},erl_parse,"bad " ++ ["record"] ++ " declaration"}],
+ []}
+ }
+ ],
+ [] = run2(Config, Ts),
+
+ {error,{{1,4},erl_parse,"bad term"}} = parse_string("1, 2 + 4."),
+ {error,{{1,1},erl_parse,"bad term"}} = parse_string("34 + begin 34 end."),
+ ok.
+
+parse_string(S) ->
+ {ok,Ts,_} = erl_scan:string(S, {1, 1}),
+ erl_parse:parse_term(Ts).
+
run(Config, Tests) ->
File = test_filename(Config),
@@ -343,7 +432,7 @@ test_filename(Conf) ->
run_test(Test0, File, Warnings, WriteBeam) ->
ModName = filename:rootname(filename:basename(File), ".erl"),
Mod = list_to_atom(ModName),
- Test = ["-module(",ModName,"). ",Test0],
+ Test = iolist_to_binary(["-module(",ModName,"). ",Test0]),
Opts = case WriteBeam of
dont_write_beam ->
[binary,return_errors|Warnings];
@@ -359,6 +448,7 @@ run_test(Test0, File, Warnings, WriteBeam) ->
io:format("~p\n", [Opts]),
Res = case compile:file(File, Opts) of
{ok,Mod,_,[{_File,Ws}]} ->
+ print_diagnostics(Ws, Test),
{warning,Ws};
{ok,Mod,_,[]} ->
[];
@@ -367,15 +457,42 @@ run_test(Test0, File, Warnings, WriteBeam) ->
{ok,Mod,[]} ->
[];
{error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) ->
+ print_diagnostics(Es, Test),
{error,Es,Ws};
{error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ
when is_list(XFile) ->
- {error,Es1++Es2,Ws};
+ Es = Es1 ++ Es2,
+ print_diagnostics(Es, Test),
+ {error,Es,Ws};
{error,Es,[{_File,Ws}]} = _ZZ->
+ print_diagnostics(Es ++ Ws, Test),
{error,Es,Ws}
end,
file:delete(File),
Res.
+print_diagnostics(Warnings, Source) ->
+ case binary:match(Source, <<"-file(">>) of
+ nomatch ->
+ Lines = binary:split(Source, <<"\n">>, [global]),
+ Cs = [print_diagnostic(W, Lines) || W <- Warnings],
+ io:put_chars(Cs);
+ _ ->
+ %% There are probably fake line numbers greater than
+ %% the number of actual lines.
+ ok
+ end.
+
+print_diagnostic({{LineNum,Column},Mod,Data}, Lines) ->
+ Line0 = lists:nth(LineNum, Lines),
+ <<Line1:(Column-1)/binary,_/binary>> = Line0,
+ Spaces = re:replace(Line1, <<"[^\t]">>, <<" ">>, [global]),
+ CaretLine = [Spaces,"^"],
+ [io_lib:format("~p:~p: ~ts\n", [LineNum,Column,Mod:format_error(Data)]),
+ Line0, "\n",
+ CaretLine, "\n\n"];
+print_diagnostic(_, _) ->
+ [].
+
fail() ->
ct:fail(failed).
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 46672c3a88..877329bd42 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -97,11 +97,11 @@ pattern(Config) when is_list(Config) ->
foo(X) ->
a = {nisse,b} = X.
">>,
- [warn_unused_vars],
- {warnings,
- [{2,v3_core,nomatch},
- {6,v3_core,nomatch},
- {11,v3_core,nomatch} ] }}],
+ [warn_unused_vars],
+ {warnings,
+ [{{2,15},v3_core,nomatch},
+ {{6,20},v3_core,nomatch},
+ {{11,20},v3_core,nomatch} ] }}],
[] = run(Config, Ts),
ok.
@@ -111,21 +111,21 @@ pattern2(Config) when is_list(Config) ->
%% v3_kernel should generate some of the warnings.
Source = <<"f(A) -> ok;
f(B) -> error.
- t(A, B, C) ->
- case {A,B,C} of
- {a,B} -> ok;
- {_,B} -> ok
- end.
+ t(A, B, C) ->
+ case {A,B,C} of
+ {a,B} -> ok;
+ {_,B} -> ok
+ end.
">>,
%% Test warnings from sys_core_fold.
Ts = [{pattern2,
Source,
[nowarn_unused_vars],
- {warnings,[{2,sys_core_fold,{nomatch_shadow,1,{f,1}}},
- {4,sys_core_fold,no_clause_match},
- {5,sys_core_fold,nomatch_clause_type},
- {6,sys_core_fold,nomatch_clause_type}]}}],
+ {warnings,[{{2,15},sys_core_fold,{nomatch_shadow,1,{f,1}}},
+ {{4,15},sys_core_fold,no_clause_match},
+ {{5,17},sys_core_fold,nomatch_clause_type},
+ {{6,17},sys_core_fold,nomatch_clause_type}]}}],
[] = run(Config, Ts),
%% Disable Core Erlang optimizations. v3_kernel should produce
@@ -134,7 +134,7 @@ pattern2(Config) when is_list(Config) ->
Source,
[nowarn_unused_vars,no_copt],
{warnings,
- [{2,v3_kernel,{nomatch_shadow,1}}]}}],
+ [{{2,15},v3_kernel,{nomatch_shadow,1}}]}}],
[] = run(Config, Ts2),
ok.
@@ -144,13 +144,13 @@ pattern3(Config) when is_list(Config) ->
Ts = [{pattern3,
<<"
- f({A,_}) -> {ok,A};
- f([_|_]=B) -> {ok,B};
- f({urk,nisse}) -> urka_glurka.
+ f({A,_}) -> {ok,A};
+ f([_|_]=B) -> {ok,B};
+ f({urk,nisse}) -> urka_glurka.
">>,
[nowarn_unused_vars],
{warnings,
- [{4,v3_kernel,{nomatch_shadow,2}}]}}],
+ [{{4,13},v3_kernel,{nomatch_shadow,2}}]}}],
[] = run(Config, Ts),
ok.
@@ -205,12 +205,12 @@ pattern4(Config) when is_list(Config) ->
">>,
[nowarn_unused_vars],
{warnings,
- [{9,sys_core_fold,no_clause_match},
- {11,sys_core_fold,nomatch_shadow},
- {15,sys_core_fold,nomatch_shadow},
- {18,sys_core_fold,no_clause_match},
- {23,sys_core_fold,no_clause_match},
- {33,sys_core_fold,no_clause_match}
+ [{{9,16},sys_core_fold,no_clause_match},
+ {{11,18},sys_core_fold,nomatch_shadow},
+ {{15,18},sys_core_fold,nomatch_shadow},
+ {{18,16},sys_core_fold,no_clause_match},
+ {{23,16},sys_core_fold,no_clause_match},
+ {{33,16},sys_core_fold,no_clause_match}
]}}],
[] = run(Config, Ts),
@@ -234,14 +234,14 @@ guard(Config) when is_list(Config) ->
">>,
[nowarn_unused_vars],
{warnings,
- [{2,sys_core_fold,no_clause_match},
- {2,sys_core_fold,nomatch_guard},
- {2,sys_core_fold,{eval_failure,badarg}},
- {4,sys_core_fold,no_clause_match},
- {4,sys_core_fold,nomatch_guard},
- {6,sys_core_fold,no_clause_match},
- {6,sys_core_fold,nomatch_guard},
- {6,sys_core_fold,{eval_failure,badarg}}
+ [{{2,15},sys_core_fold,no_clause_match},
+ {{2,15},sys_core_fold,nomatch_guard},
+ {{2,28},sys_core_fold,{eval_failure,badarg}},
+ {{4,15},sys_core_fold,no_clause_match},
+ {{4,15},sys_core_fold,nomatch_guard},
+ {{6,15},sys_core_fold,no_clause_match},
+ {{6,15},sys_core_fold,nomatch_guard},
+ {{6,26},sys_core_fold,{eval_failure,badarg}}
]}}],
[] = run(Config, Ts),
@@ -252,14 +252,14 @@ bad_arith(Config) when is_list(Config) ->
<<"f() ->
if
a + 3 > 3 -> ok;
- true -> error
+ true -> error
end.
g(A) ->
if
is_integer(A), a + 3 > 3 -> ok;
a + 3 > 42, is_integer(A) -> ok;
- true -> error
+ true -> error
end.
h(A) ->
@@ -267,14 +267,14 @@ bad_arith(Config) when is_list(Config) ->
">>,
[],
{warnings,
- [{3,sys_core_fold,nomatch_guard},
- {3,sys_core_fold,{eval_failure,badarith}},
- {9,sys_core_fold,nomatch_guard},
- {9,sys_core_fold,{eval_failure,badarith}},
- {9,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
- {10,sys_core_fold,nomatch_guard},
- {10,sys_core_fold,{eval_failure,badarith}},
- {15,sys_core_fold,{eval_failure,badarith}}
+ [{{3,19},sys_core_fold,nomatch_guard},
+ {{3,21},sys_core_fold,{eval_failure,badarith}},
+ {{9,19},sys_core_fold,nomatch_guard},
+ {{9,19},sys_core_fold,{no_effect,{erlang,is_integer,1}}},
+ {{9,36},sys_core_fold,{eval_failure,badarith}},
+ {{10,19},sys_core_fold,nomatch_guard},
+ {{10,21},sys_core_fold,{eval_failure,badarith}},
+ {{15,19},sys_core_fold,{eval_failure,badarith}}
] }}],
[] = run(Config, Ts),
ok.
@@ -296,18 +296,18 @@ bool_cases(Config) when is_list(Config) ->
Other -> {error,not_bool}
end.
- h(Bool) ->
- case not Bool of
- maybe -> strange;
- false -> ok;
- true -> error
- end.
+ h(Bool) ->
+ case not Bool of
+ maybe -> strange;
+ false -> ok;
+ true -> error
+ end.
">>,
- [nowarn_unused_vars],
- {warnings,
- [{6,sys_core_fold,nomatch_shadow},
- {13,sys_core_fold,nomatch_shadow},
- {18,sys_core_fold,nomatch_clause_type} ]} }],
+ [nowarn_unused_vars],
+ {warnings,
+ [{{6,18},sys_core_fold,nomatch_shadow},
+ {{13,18},sys_core_fold,nomatch_shadow},
+ {{18,18},sys_core_fold,nomatch_clause_type} ]} }],
[] = run(Config, Ts),
ok.
@@ -320,13 +320,13 @@ bad_apply(Config) when is_list(Config) ->
t(4) -> []:start();
t(5) -> erlang:[]().
">>,
- [],
- {warnings,
- [{2,v3_kernel,bad_call},
- {3,v3_kernel,bad_call},
- {4,v3_kernel,bad_call},
- {5,v3_kernel,bad_call},
- {6,v3_kernel,bad_call}]}}],
+ [],
+ {warnings,
+ [{{2,22},v3_kernel,bad_call},
+ {{3,22},v3_kernel,bad_call},
+ {{4,22},v3_kernel,bad_call},
+ {{5,22},v3_kernel,bad_call},
+ {{6,22},v3_kernel,bad_call}]}}],
[] = run(Config, Ts),
%% Also verify that the generated code generates the correct error.
@@ -352,108 +352,63 @@ files(Config) when is_list(Config) ->
">>,
[],
{warnings,
- [{"file1",[{17,sys_core_fold,{eval_failure,badarith}}]},
- {"file2",[{10,sys_core_fold,{eval_failure,badarith}}]}]}}],
+ [{"file1",[{{17,20},sys_core_fold,{eval_failure,badarith}}]},
+ {"file2",[{{10,20},sys_core_fold,{eval_failure,badarith}}]}]}}],
[] = run(Config, Ts),
ok.
%% Test warnings for term construction and BIF calls in effect context.
effect(Config) when is_list(Config) ->
- Ts = [{effect,
- <<"
- t(X) ->
- case X of
- warn_lc ->
- [is_integer(Z) || Z <- [1,2,3]];
- warn_lc_2 ->
- [{error,Z} || Z <- [1,2,3]];
- warn_lc_3 ->
- [{error,abs(Z)} || Z <- [1,2,3]];
- no_warn_lc ->
- [put(last_integer, Z) || Z <- [1,2,3]]; %no warning
- unused_tuple_literal ->
- {a,b,c};
- unused_list_literal ->
- [1,2,3,4];
- unused_integer ->
- 42;
- unused_arith ->
- X*X;
- nested ->
- [{ok,node(),?MODULE:foo(),self(),[time(),date()],time()},
- is_integer(X)];
- unused_bit_syntax ->
- <<X:8>>;
- unused_fun ->
- fun() -> {ok,X} end;
- unused_named_fun ->
- fun F(0) -> 1;
- F(N) -> N*F(N-1)
- end;
- unused_atom ->
- ignore; %no warning
- unused_nil ->
- []; %no warning
- comp_op ->
- X =:= 2;
- cookie ->
- erlang:get_cookie();
- result_ignore ->
- _ = list_to_integer(X);
- warn_lc_4 ->
- %% No warning because of assignment to _.
- [_ = abs(Z) || Z <- [1,2,3]]
- end,
- ok.
-
- %% No warnings should be generated in the following functions.
+ Ts = [{no_warnings,
+ %% No warnings should be generated in the following functions.
+ <<"
m1(X, Sz) ->
if
- Sz =:= 0 -> X = 0;
- true -> ok
+ Sz =:= 0 -> X = 0;
+ true -> ok
end,
ok.
m2(X, Sz) ->
if
- Sz =:= 0 -> X = {a,Sz};
- true -> ok
+ Sz =:= 0 -> X = {a,Sz};
+ true -> ok
end,
ok.
m3(X, Sz) ->
if
- Sz =:= 0 -> X = [a,Sz];
- true -> ok
+ Sz =:= 0 -> X = [a,Sz];
+ true -> ok
end,
ok.
m4(X, Sz, Var) ->
if
- Sz =:= 0 -> X = Var;
- true -> ok
+ Sz =:= 0 -> X = Var;
+ true -> ok
end,
ok.
m5(X, Sz) ->
if
- Sz =:= 0 -> X = {a,b,c};
- true -> ok
+ Sz =:= 0 -> X = {a,b,c};
+ true -> ok
end,
ok.
m6(X, Sz) ->
if
- Sz =:= 0 -> X = {a,Sz,[1,2,3]};
- true -> ok
+ Sz =:= 0 -> X = {a,Sz,[1,2,3]};
+ true -> ok
end,
ok.
m7(X, Sz) ->
if
- Sz =:= 0 -> X = {a,Sz,[1,2,3],abs(Sz)};
- true -> ok
+ Sz =:= 0 -> X = {a,Sz,[1,2,3],abs(Sz)};
+ true -> ok
end,
ok.
@@ -473,34 +428,103 @@ effect(Config) when is_list(Config) ->
CurrentConfig = {id(camel_phase3),id(sms)},
case CurrentConfig of
{apa, bepa} -> ok;
- _ -> ok
- end
+ _ -> ok
+ end
end,
ok.
id(I) -> I.
">>,
- [],
- {warnings,[{5,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
- {7,sys_core_fold,useless_building},
- {9,sys_core_fold,result_ignored},
- {9,sys_core_fold,useless_building},
- {13,sys_core_fold,useless_building},
- {15,sys_core_fold,useless_building},
- {17,sys_core_fold,useless_building},
- {19,sys_core_fold,result_ignored},
- {21,sys_core_fold,useless_building},
- {21,sys_core_fold,{no_effect,{erlang,date,0}}},
- {21,sys_core_fold,{no_effect,{erlang,node,0}}},
- {21,sys_core_fold,{no_effect,{erlang,self,0}}},
- {21,sys_core_fold,{no_effect,{erlang,time,0}}},
- {22,sys_core_fold,useless_building},
- {22,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
- {24,sys_core_fold,useless_building},
- {26,sys_core_fold,useless_building},
- {28,sys_core_fold,useless_building},
- {36,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
- {38,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
+ [],[]},
+
+ {basic,
+ <<"
+ t(X) ->
+ case X of
+ warn_lc ->
+ [is_integer(Z) || Z <- [1,2,3]];
+ warn_lc_2 ->
+ [{error,Z} || Z <- [1,2,3]];
+ warn_lc_3 ->
+ [{error,abs(Z)} || Z <- [1,2,3]];
+ no_warn_lc ->
+ [put(last_integer, Z) || Z <- [1,2,3]]; %no warning
+ unused_tuple_literal ->
+ {a,b,c};
+ unused_list_literal ->
+ [1,2,3,4];
+ unused_integer ->
+ 42;
+ unused_arith ->
+ X*X
+ end,
+ ok.
+ ">>,
+ [],
+ {warnings,[{{5,22},sys_core_fold,{no_effect,{erlang,is_integer,1}}},
+ {{7,22},sys_core_fold,useless_building},
+ {{9,22},sys_core_fold,useless_building},
+ {{9,29},sys_core_fold,result_ignored},
+ {{13,21},sys_core_fold,useless_building},
+ {{15,21},sys_core_fold,useless_building},
+ {{17,21},sys_core_fold,useless_building},
+ {{19,22},sys_core_fold,result_ignored}]}},
+
+ {nested,
+ <<"
+ t(X) ->
+ case X of
+ nested ->
+ [{ok,node(),module:foo(),self(),[time(),date()],time()},
+ is_integer(X)];
+ unused_bit_syntax ->
+ <<X:8>>;
+ unused_fun ->
+ fun() -> {ok,X} end;
+ unused_named_fun ->
+ fun F(0) -> 1;
+ F(N) -> N*F(N-1)
+ end;
+ unused_atom ->
+ ignore; %no warning
+ unused_nil ->
+ []; %no warning
+ comp_op ->
+ X =:= 2;
+ cookie ->
+ erlang:get_cookie();
+ result_ignore ->
+ _ = list_to_integer(X);
+ warn_lc_4 ->
+ %% No warning because of assignment to _.
+ [_ = abs(Z) || Z <- [1,2,3]]
+ end,
+ ok.
+ ">>,
+ [],
+ {warnings,[{{5,21},sys_core_fold,useless_building},
+ {{5,26},sys_core_fold,{no_effect,{erlang,node,0}}},
+ {{5,46},sys_core_fold,{no_effect,{erlang,self,0}}},
+ {{5,54},sys_core_fold,{no_effect,{erlang,time,0}}},
+ {{5,61},sys_core_fold,{no_effect,{erlang,date,0}}},
+ {{5,69},sys_core_fold,{no_effect,{erlang,time,0}}},
+ {{6,22},sys_core_fold,{no_effect,{erlang,is_integer,1}}},
+ {{8,21},sys_core_fold,useless_building},
+ {{10,21},sys_core_fold,useless_building},
+ {{12,21},sys_core_fold,useless_building},
+ {{20,23},sys_core_fold,{no_effect,{erlang,'=:=',2}}},
+ {{22,21},sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}},
+
+ {seq,
+ <<"
+ t(T) ->
+ [ {a,b,T} ], [ {x,y,T} ],
+ ok.
+ ">>,
+ [],
+ {warnings,[{{3,16},sys_core_fold,useless_building},
+ {{3,30},sys_core_fold,useless_building}]}}
+ ],
[] = run(Config, Ts),
ok.
@@ -508,9 +532,9 @@ bin_opt_info(Config) when is_list(Config) ->
Code = <<"
t1(Bin) ->
case Bin of
- _ when byte_size(Bin) > 20 -> erlang:error(too_long);
+ _ when byte_size(Bin) > 20 -> erlang:error(too_long);
<<_,T/binary>> -> t1(T);
- <<>> -> ok
+ <<>> -> ok
end.
%% We use a tail in a BIF instruction, remote call, function
@@ -533,17 +557,17 @@ bin_opt_info(Config) when is_list(Config) ->
%% to run.
{warnings,
[{5,beam_ssa_bsm,{unsuitable_call,
- {{b_local,{b_literal,t1},1},
- {used_before_match,
- {b_set,_,_,{bif,byte_size},[_]}}}}},
+ {{b_local,{b_literal,t1},1},
+ {used_before_match,
+ {b_set,_,_,{bif,byte_size},[_]}}}}},
{5,beam_ssa_bsm,{binary_created,_,_}},
{11,beam_ssa_bsm,{binary_created,_,_}}, %% A =< B -> T
{13,beam_ssa_bsm,context_reused}, %% A > B -> t2(T);
{16,beam_ssa_bsm,{binary_created,_,_}}, %% when byte_size(T) < 4 ->
{19,beam_ssa_bsm,{remote_call,
- {b_remote,
- {b_literal,erlang},
- {b_literal,split_binary},2}}},
+ {b_remote,
+ {b_literal,erlang},
+ {b_literal,split_binary},2}}},
{19,beam_ssa_bsm,{binary_created,_,_}} %% split_binary(T, 4)
]} = Ws,
@@ -563,9 +587,9 @@ bin_construction(Config) when is_list(Config) ->
Bin = <<1,2,3,7:4>>,
<<Bin/binary>>.
">>,
- [],
- {warnings,[{4,sys_core_fold,embedded_binary_size},
- {8,sys_core_fold,{embedded_unit,8,28}}]}}],
+ [],
+ {warnings,[{{4,18},sys_core_fold,embedded_binary_size},
+ {{8,18},sys_core_fold,{embedded_unit,8,28}}]}}],
[] = run(Config, Ts),
ok.
@@ -595,17 +619,17 @@ maps(Config) when is_list(Config) ->
end.
">>,
[],
- {warnings,[{3,sys_core_fold,no_clause_match},
- {9,sys_core_fold,nomatch_clause_type}]}},
+ {warnings,[{{3,18},sys_core_fold,no_clause_match},
+ {{9,22},sys_core_fold,nomatch_clause_type}]}},
{bad_map_src1,
<<"
t() ->
- M = {a,[]},
- {'EXIT',{badarg,_}} = (catch(M#{ a => 1 })),
- ok.
+ M = {a,[]},
+ {'EXIT',{badarg,_}} = (catch(M#{ a => 1 })),
+ ok.
">>,
[],
- {warnings,[{4,sys_core_fold,{eval_failure,badmap}}]}},
+ {warnings,[{{4,48},sys_core_fold,{eval_failure,badmap}}]}},
{bad_map_src2,
<<"
t() ->
@@ -619,11 +643,11 @@ maps(Config) when is_list(Config) ->
{bad_map_src3,
<<"
t() ->
- {'EXIT',{badarg,_}} = (catch <<>>#{ a := 1}),
- ok.
+ {'EXIT',{badarg,_}} = (catch <<>>#{ a := 1}),
+ ok.
">>,
[],
- {warnings,[{3,sys_core_fold,{eval_failure,badmap}}]}},
+ {warnings,[{{3,51},sys_core_fold,{eval_failure,badmap}}]}},
{ok_map_literal_key,
<<"
t() ->
@@ -672,15 +696,15 @@ maps(Config) when is_list(Config) ->
M#{<<\"a\">>=>1, <<\"b\">>=> 2, <<\"a\">>:=3}.
">>,
[],
- {warnings,[{3,v3_core,{map_key_repeated,a}},
- {8,v3_core,{map_key_repeated,a}},
- {11,v3_core,{map_key_repeated,a}},
- {14,v3_core,{map_key_repeated,"a"}},
- {17,v3_core,{map_key_repeated,"a"}},
- {20,v3_core,{map_key_repeated,"a"}},
- {23,v3_core,{map_key_repeated,"a"}},
- {28,v3_core,{map_key_repeated,"a"}},
- {31,v3_core,{map_key_repeated,<<"a">>}}]}},
+ {warnings,[{{3,20},v3_core,{map_key_repeated,a}},
+ {{8,21},v3_core,{map_key_repeated,a}},
+ {{11,21},v3_core,{map_key_repeated,a}},
+ {{14,20},v3_core,{map_key_repeated,"a"}},
+ {{17,21},v3_core,{map_key_repeated,"a"}},
+ {{20,21},v3_core,{map_key_repeated,"a"}},
+ {{23,20},v3_core,{map_key_repeated,"a"}},
+ {{28,21},v3_core,{map_key_repeated,"a"}},
+ {{31,21},v3_core,{map_key_repeated,<<"a">>}}]}},
{repeated_keys2,
<<"
foo4(K) ->
@@ -733,15 +757,15 @@ maps(Config) when is_list(Config) ->
M1#{#{<<\"a\">>=>1}:=3,K=>2}.
">>,
[],
- {warnings,[{3,v3_core,{map_key_repeated,"a"}},
- {6,v3_core,{map_key_repeated,a}},
- {9,v3_core,{map_key_repeated,<<"a">>}},
- {14,v3_core,{map_key_repeated,{"a",1}}},
- {17,v3_core,{map_key_repeated,{"a",<<"b">>}}},
- {21,v3_core,{map_key_repeated,{<<"a">>,1}}},
- {25,v3_core,{map_key_repeated,#{"a" => 1}}},
- {28,v3_core,{map_key_repeated,#{"a" => <<"b">>}}},
- {32,v3_core,{map_key_repeated,#{<<"a">> => 1}}}]}}
+ {warnings,[{{3,20},v3_core,{map_key_repeated,"a"}},
+ {{6,21},v3_core,{map_key_repeated,a}},
+ {{9,21},v3_core,{map_key_repeated,<<"a">>}},
+ {{14,20},v3_core,{map_key_repeated,{"a",1}}},
+ {{17,21},v3_core,{map_key_repeated,{"a",<<"b">>}}},
+ {{21,21},v3_core,{map_key_repeated,{<<"a">>,1}}},
+ {{25,20},v3_core,{map_key_repeated,#{"a" => 1}}},
+ {{28,21},v3_core,{map_key_repeated,#{"a" => <<"b">>}}},
+ {{32,21},v3_core,{map_key_repeated,#{<<"a">> => 1}}}]}}
],
run(Config, Ts),
ok.
@@ -770,7 +794,7 @@ redundant_boolean_clauses(Config) when is_list(Config) ->
end.
">>,
[],
- {warnings,[{5,sys_core_fold,nomatch_shadow}]}}],
+ {warnings,[{{5,22},sys_core_fold,nomatch_shadow}]}}],
run(Config, Ts),
ok.
@@ -782,13 +806,13 @@ latin1_fallback(Conf) when is_list(Conf) ->
%% Test that the compiler fall backs to latin-1 with
%% a warning if a file has no encoding and does not
%% contain correct UTF-8 sequences.
- <<"%% Bj",246,"rn
+ <<"\n%% Bj",246,"rn
t(_) -> \"",246,"\";
t(x) -> ok.
">>,
- [],
- {warnings,[{1,compile,reparsing_invalid_unicode},
- {3,sys_core_fold,{nomatch_shadow,2,{t,1}}}]}}],
+ [],
+ {warnings,[{{2,1},compile,reparsing_invalid_unicode},
+ {{4,15},sys_core_fold,{nomatch_shadow,3,{t,1}}}]}}],
[] = run(Conf, Ts1),
Ts2 = [{latin1_fallback2,
@@ -800,7 +824,7 @@ latin1_fallback(Conf) when is_list(Conf) ->
-include(\"include_me.hrl\").
">>,
[],
- {warnings,[{1,compile,reparsing_invalid_unicode}]}
+ {warnings,[{{1,1},compile,reparsing_invalid_unicode}]}
}],
[] = run(Conf, Ts2),
@@ -814,38 +838,39 @@ latin1_fallback(Conf) when is_list(Conf) ->
-endif.
">>,
[],
- {warnings,[{2,compile,reparsing_invalid_unicode}]}}],
+ {warnings,[{{2,24},compile,reparsing_invalid_unicode}]}}],
[] = run(Conf, Ts3),
ok.
underscore(Config) when is_list(Config) ->
%% The code template.
- S0 = <<"f(A) ->
+ S0 = <<"
+ f(A) ->
_VAR1 = <<A>>,
_VAR2 = {ok,A},
_VAR3 = [A],
ok.
- g(A) ->
+ g(A) ->
_VAR1 = A/0,
_VAR2 = date(),
- ok.
+ ok.
h() ->
_VAR1 = fun() -> ok end,
- ok.
+ ok.
i(A) ->
_VAR1 = #{A=>42},
- ok.
+ ok.
">>,
%% Define all possible warnings.
- Warnings = [{2,sys_core_fold,useless_building},
- {3,sys_core_fold,useless_building},
- {4,sys_core_fold,useless_building},
- {7,sys_core_fold,result_ignored},
- {8,sys_core_fold,{no_effect,{erlang,date,0}}},
- {11,sys_core_fold,useless_building},
- {14,sys_core_fold,useless_building}],
+ Warnings = [{{3,23},sys_core_fold,useless_building},
+ {{4,23},sys_core_fold,useless_building},
+ {{5,23},sys_core_fold,useless_building},
+ {{8,24},sys_core_fold,result_ignored},
+ {{9,23},sys_core_fold,{no_effect,{erlang,date,0}}},
+ {{12,24},sys_core_fold,useless_building},
+ {{15,24},sys_core_fold,useless_building}],
%% Compile the unmodified template. Assigning to variable that
@@ -861,7 +886,7 @@ underscore(Config) when is_list(Config) ->
[] = run(Config, Ts1),
%% Make sure that we get warnings if we remove "_VAR<digit> = ".
- S2 = re:replace(S0, "_VAR\\d+ = ", "", [global]),
+ S2 = re:replace(S0, "_VAR\\d = ", " ", [global]),
io:format("~s\n", [S2]),
Ts2 = [{underscore2,S2,[],{warnings,Warnings}}],
[] = run(Config, Ts2),
@@ -869,7 +894,7 @@ underscore(Config) when is_list(Config) ->
%% We should also get warnings if we assign to a variables that don't
%% begin with underscore (as well as warnings for unused variables from
%% erl_lint).
- S3 = re:replace(S0, "_(?=VAR\\d+)", "", [global]),
+ S3 = re:replace(S0, "_(?=VAR\\d+)", " ", [global]),
io:format("~s\n", [S3]),
Ts3 = [{underscore2,S3,[],{warnings,Warnings}}],
[] = run(Config, Ts3),
@@ -913,7 +938,8 @@ no_warnings(Config) when is_list(Config) ->
bit_syntax(Config) ->
Ts = [{?FUNCTION_NAME,
- <<"a(<<-1>>) -> ok;
+ <<"
+ a(<<-1>>) -> ok;
a(<<1023>>) -> ok;
a(<<777/signed>>) -> ok;
a(<<a/binary>>) -> ok;
@@ -932,25 +958,25 @@ bit_syntax(Config) ->
end.
">>,
[],
- {warnings,[{1,sys_core_fold,no_clause_match},
- {1,sys_core_fold,{nomatch_bit_syntax_unsigned,-1}},
- {2,sys_core_fold,{nomatch_bit_syntax_truncated,
- unsigned,1023,8}},
- {3,sys_core_fold,{nomatch_bit_syntax_truncated,
- signed,777,8}},
- {4,sys_core_fold,{nomatch_bit_syntax_type,a,binary}},
- {5,sys_core_fold,{nomatch_bit_syntax_type,a,integer}},
- {6,sys_core_fold,{nomatch_bit_syntax_type,a,float}},
- {7,sys_core_fold,{nomatch_bit_syntax_type,a,utf8}},
- {8,sys_core_fold,{nomatch_bit_syntax_type,a,utf16}},
- {9,sys_core_fold,{nomatch_bit_syntax_type,a,utf32}},
- {10,sys_core_fold,{nomatch_bit_syntax_type,a,utf32}},
- {11,sys_core_fold,no_clause_match},
- {11,sys_core_fold,{nomatch_bit_syntax_size,bad}},
- {14,sys_core_fold,{nomatch_bit_syntax_unsigned,-42}},
- {16,sys_core_fold,{nomatch_bit_syntax_type,42,binary}}
- ]}
- }],
+ {warnings,[{{2,15},sys_core_fold,no_clause_match},
+ {{2,19},sys_core_fold,{nomatch_bit_syntax_unsigned,-1}},
+ {{3,19},sys_core_fold,{nomatch_bit_syntax_truncated,
+ unsigned,1023,8}},
+ {{4,19},sys_core_fold,{nomatch_bit_syntax_truncated,
+ signed,777,8}},
+ {{5,19},sys_core_fold,{nomatch_bit_syntax_type,a,binary}},
+ {{6,19},sys_core_fold,{nomatch_bit_syntax_type,a,integer}},
+ {{7,19},sys_core_fold,{nomatch_bit_syntax_type,a,float}},
+ {{8,19},sys_core_fold,{nomatch_bit_syntax_type,a,utf8}},
+ {{9,19},sys_core_fold,{nomatch_bit_syntax_type,a,utf16}},
+ {{10,19},sys_core_fold,{nomatch_bit_syntax_type,a,utf32}},
+ {{11,19},sys_core_fold,{nomatch_bit_syntax_type,a,utf32}},
+ {{12,37},sys_core_fold,{nomatch_bit_syntax_size,bad}},
+ {{12,45},sys_core_fold,no_clause_match},
+ {{15,21},sys_core_fold,{nomatch_bit_syntax_unsigned,-42}},
+ {{17,21},sys_core_fold,{nomatch_bit_syntax_type,42,binary}}
+ ]}
+ }],
run(Config, Ts),
ok.
@@ -1053,8 +1079,33 @@ recv_opt_info(Config) when is_list(Config) ->
%%% End of test cases.
%%%
-run(Config, Tests) ->
+run(Config, Tests0) ->
+ do_run(Config, Tests0),
+
+ %% Now test without column numbers.
+ Tests = [lines_only(T) || T <- Tests0],
+ do_run(Config, Tests).
+
+lines_only({Name,Test,Opts,{warnings,Result0}}) ->
+ Result1 = lists:map(fun lines_only_1/1, Result0),
+ Result = {warnings,lists:usort(Result1)},
+ {Name,Test,[{error_location,line}|Opts],Result};
+lines_only(NoWarnings) -> NoWarnings.
+
+lines_only_1({File,Es0}) when is_list(Es0) ->
+ Es = [lines_only_1(E) || E <- Es0],
+ {File,Es};
+lines_only_1({Loc,Mod,Error}) ->
+ case Loc of
+ {Line,_Col} ->
+ {Line,Mod,Error};
+ Line when is_integer(Line) ->
+ {Line,Mod,Error}
+ end.
+
+do_run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
+ io:format("### ~s\n", [N]),
case catch run_test(Config, P, Ws) of
E ->
BadL;
@@ -1069,15 +1120,16 @@ run(Config, Tests) ->
%% Compiles a test module and returns the list of errors and warnings.
run_test(Conf, Test0, Warnings) ->
- Module = "warnings_"++test_lib:uniq(),
+ Module = "warnings" ++ test_lib:uniq(),
Filename = Module ++ ".erl",
DataDir = ?privdir,
- Test = ["-module(", Module, "). ", Test0],
+ Test1 = ["-module(", Module, "). -file( \"", Filename, "\", 1). ", Test0],
+ Test = iolist_to_binary(Test1),
File = filename:join(DataDir, Filename),
Opts = [binary,export_all,return|Warnings],
ok = file:write_file(File, Test),
- %% Compile once just to print all warnings.
+ %% Compile once just to print all warnings (and cover more code).
compile:file(File, [binary,export_all,report|Warnings]),
%% Test result of compilation.
@@ -1091,12 +1143,33 @@ run_test(Conf, Test0, Warnings) ->
Mod =/= erl_lint]} ||
{F,Ws} <- Ws0],
case WsL of
- [{_File,Ws}] -> {warnings, Ws};
- _ -> list_to_tuple([warnings, WsL])
+ [{_File,Ws}] ->
+ print_warnings(Ws, Test),
+ {warnings, Ws};
+ _ ->
+ list_to_tuple([warnings, WsL])
end
end,
file:delete(File),
Res.
+print_warnings(Warnings, Source) ->
+ Lines = binary:split(Source, <<"\n">>, [global]),
+ Cs = [print_warning(W, Lines) || W <- Warnings],
+ io:put_chars(Cs),
+ ok.
+
+print_warning({{LineNum,Column},Mod,Data}, Lines) ->
+ Line0 = lists:nth(LineNum, Lines),
+ <<Line1:(Column-1)/binary,_/binary>> = Line0,
+ Spaces = re:replace(Line1, <<"[^\t]">>, <<" ">>, [global]),
+ CaretLine = [Spaces,"^"],
+ [io_lib:format("~p:~p: ~ts\n",
+ [LineNum,Column,Mod:format_error(Data)]),
+ Line0, "\n",
+ CaretLine, "\n\n"];
+print_warning(_, _) ->
+ [].
+
fail() ->
ct:fail(failed).