%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% -module(error_SUITE). -include_lib("test_server/include/test_server.hrl"). -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,forbidden_maps/1,bad_utf8/1]). %% Used by transforms/1 test case. -export([parse_transform/2]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> test_lib:recompile(?MODULE), [{group,p}]. groups() -> [{p,test_lib:parallel(), [head_mismatch_line,warnings_as_errors,bif_clashes, transforms,forbidden_maps,bad_utf8]}]. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. bif_clashes(Config) when is_list(Config) -> Ts = [{bif_clashes1, <<" -export([t/0]). t() -> length([a,b,c]). length(X) -> erlang:length(X). ">>, [return_warnings], {error, [{4, erl_lint,{call_to_redefined_old_bif,{length,1}}}], []} }], ?line [] = run(Config, Ts), Ts1 = [{bif_clashes2, <<" -export([t/0]). -import(x,[length/1]). t() -> length([a,b,c]). ">>, [return_warnings], {error, [{3, erl_lint,{redefine_old_bif_import,{length,1}}}], []} }], ?line [] = run(Config, Ts1), Ts00 = [{bif_clashes3, <<" -export([t/0]). -compile({no_auto_import,[length/1]}). t() -> length([a,b,c]). length(X) -> erlang:length(X). ">>, [return_warnings], []}], ?line [] = run(Config, Ts00), Ts11 = [{bif_clashes4, <<" -export([t/0]). -compile({no_auto_import,[length/1]}). -import(x,[length/1]). t() -> length([a,b,c]). ">>, [return_warnings], []}], ?line [] = run(Config, Ts11), Ts000 = [{bif_clashes5, <<" -export([t/0]). t() -> binary_part(<<1,2,3,4>>,1,2). binary_part(X,Y,Z) -> erlang:binary_part(X,Y,Z). ">>, [return_warnings], {warning, [{4, erl_lint,{call_to_redefined_bif,{binary_part,3}}}]} }], ?line [] = run(Config, Ts000), Ts111 = [{bif_clashes6, <<" -export([t/0]). -import(x,[binary_part/3]). t() -> binary_part(<<1,2,3,4>>,1,2). ">>, [return_warnings], {warning, [{3, erl_lint,{redefine_bif_import,{binary_part,3}}}]} }], ?line [] = run(Config, Ts111), Ts2 = [{bif_clashes7, <<" -export([t/0]). -compile({no_auto_import,[length/1]}). -import(x,[length/1]). t() -> length([a,b,c]). length(X) -> erlang:length(X). ">>, [], {error, [{7,erl_lint,{define_import,{length,1}}}], []} }], ?line [] = run2(Config, Ts2), Ts3 = [{bif_clashes8, <<" -export([t/1]). -compile({no_auto_import,[length/1]}). t(X) when length(X) > 3 -> length([a,b,c]). length(X) -> erlang:length(X). ">>, [], {error, [{4,erl_lint,{illegal_guard_local_call,{length,1}}}], []} }], ?line [] = run2(Config, Ts3), Ts4 = [{bif_clashes9, <<" -export([t/1]). -compile({no_auto_import,[length/1]}). -import(x,[length/1]). t(X) when length(X) > 3 -> length([a,b,c]). ">>, [], {error, [{5,erl_lint,{illegal_guard_local_call,{length,1}}}], []} }], ?line [] = run2(Config, Ts4), ok. %% Tests that a head mismatch is reported on the correct line (OTP-2125). head_mismatch_line(Config) when is_list(Config) -> ?line [E|_] = get_compilation_errors(Config, "head_mismatch_line"), ?line {26, Mod, Reason} = E, ?line Mod:format_error(Reason), ok. %% Compiles a test file and returns the list of errors. get_compilation_errors(Config, Filename) -> ?line DataDir = ?config(data_dir, Config), ?line File = filename:join(DataDir, Filename), ?line {error, [{_Name, E}|_], []} = compile:file(File, [return_errors]), E. warnings_as_errors(Config) when is_list(Config) -> ?line TestFile = test_filename(Config), ?line BeamFile = filename:rootname(TestFile, ".erl") ++ ".beam", ?line OutDir = ?config(priv_dir, Config), Ts1 = [{warnings_as_errors, <<" t() -> A = unused, ok. ">>, [warnings_as_errors, export_all, {outdir, OutDir}], {error, [], [{3,erl_lint,{unused_var,'A'}}]} }], ?line [] = run(Ts1, TestFile, write_beam), ?line false = filelib:is_regular(BeamFile), Ts2 = [{warning_unused_var, <<" t() -> A = unused, ok. ">>, [return_warnings, export_all, {outdir, OutDir}], {warning, [{3,erl_lint,{unused_var,'A'}}]} }], ?line [] = run(Ts2, TestFile, write_beam), ?line true = filelib:is_regular(BeamFile), ?line ok = file:delete(BeamFile), ok. transforms(Config) -> Ts1 = [{undef_parse_transform, <<" -compile({parse_transform,non_existing}). ">>, [return], {error,[{none,compile,{undef_parse_transform,non_existing}}],[]}}], [] = run(Config, Ts1), Ts2 = <<" -compile({parse_transform,",?MODULE_STRING,"}). ">>, {error,[{none,compile,{parse_transform,?MODULE,{too_bad,_}}}],[]} = run_test(Ts2, test_filename(Config), [], dont_write_beam), Ts3 = <<" -compile({parse_transform,",?MODULE_STRING,"}). ">>, {error,[{none,compile,{parse_transform,?MODULE,{undef,_}}}],[]} = run_test(Ts3, test_filename(Config), [call_undef], dont_write_beam), ok. parse_transform(_, Opts) -> case lists:member(call_undef, Opts) of false -> error(too_bad); true -> camembert:délicieux() end. forbidden_maps(Config) when is_list(Config) -> Ts1 = [{map_illegal_use_of_pattern, <<" -export([t/0]). t() -> V = 32, #{<<\"hi\",V,\"all\">> := 1} = id(#{<<\"hi all\">> => 1}). id(I) -> I. ">>, [return], {error,[{5,erl_lint,{illegal_map_key_variable,'V'}}], []}}], [] = run2(Config, Ts1), ok. bad_utf8(Config) -> Ts = [{bad_utf8, %% If coding is specified explicitly as utf-8, there should be %% a compilation error; we must not fallback to parsing the %% file in latin-1 mode. <<"%% coding: utf-8 %% Bj",246,"rn t() -> \"",246,"\". ">>, [], {error,[{2,epp,cannot_parse}, {2,file_io_server,invalid_unicode}], []} }], [] = run2(Config, Ts), ok. run(Config, Tests) -> ?line File = test_filename(Config), run(Tests, File, dont_write_beam). run(Tests, File, WriteBeam) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(P, File, Ws, WriteBeam) of E -> BadL; Bad -> ?t:format("~nTest ~p failed. Expected~n ~p~n" "but got~n ~p~n", [N, E, Bad]), fail() end end, lists:foldl(F, [], Tests). run2(Config, Tests) -> ?line File = test_filename(Config), run2(Tests, File, dont_write_beam). run2(Tests, File, WriteBeam) -> F = fun({N,P,Ws,E}, BadL) -> case catch filter(run_test(P, File, Ws, WriteBeam)) of E -> BadL; Bad -> ?t:format("~nTest ~p failed. Expected~n ~p~n" "but got~n ~p~n", [N, E, Bad]), fail() end end, lists:foldl(F, [], Tests). filter({error,Es,_Ws}) -> {error,Es,[]}; filter(X) -> X. %% Compiles a test module and returns the list of errors and warnings. test_filename(Conf) -> Filename = ["errors_test_",test_lib:uniq(),".erl"], DataDir = ?config(priv_dir, Conf), filename:join(DataDir, Filename). run_test(Test0, File, Warnings, WriteBeam) -> ModName = filename:rootname(filename:basename(File), ".erl"), Mod = list_to_atom(ModName), Test = ["-module(",ModName,"). ",Test0], ?line Opts = case WriteBeam of dont_write_beam -> [binary,return_errors|Warnings]; write_beam -> [return_errors|Warnings] end, ?line ok = file:write_file(File, Test), %% Compile once just to print all errors and warnings. ?line compile:file(File, [binary,report|Warnings]), %% Test result of compilation. io:format("~p\n", [Opts]), ?line Res = case compile:file(File, Opts) of {ok,Mod,_,[{_File,Ws}]} -> %io:format("compile:file(~s,~p) ->~n~p~n", % [File,Opts,Ws]), {warning,Ws}; {ok,Mod,_,[]} -> %io:format("compile:file(~s,~p) ->~n~p~n", % [File,Opts,Ws]), []; {ok,Mod,[{_File,Ws}]} -> {warning,Ws}; {ok,Mod,[]} -> []; {error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) -> %io:format("compile:file(~s,~p) ->~n~p~n", % [File,Opts,_ZZ]), {error,Es,Ws}; {error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ when is_list(XFile) -> %io:format("compile:file(~s,~p) ->~n~p~n", % [File,Opts,_ZZ]), {error,Es1++Es2,Ws}; {error,Es,[{_File,Ws}]} = _ZZ-> %io:format("compile:file(~s,~p) ->~n~p~n", % [File,Opts,_ZZ]), {error,Es,Ws} end, file:delete(File), Res. fail() -> io:format("failed~n"), ?t:fail().