1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1998-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19-module(error_SUITE). 20 21-include_lib("common_test/include/ct.hrl"). 22 23-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 24 init_per_group/2,end_per_group/2, 25 head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1, 26 transforms/1,maps_warnings/1,bad_utf8/1,bad_decls/1]). 27 28%% Used by transforms/1 test case. 29-export([parse_transform/2]). 30 31suite() -> [{ct_hooks,[ts_install_cth]}]. 32 33all() -> 34 [{group,p}]. 35 36groups() -> 37 [{p,test_lib:parallel(), 38 [head_mismatch_line,warnings_as_errors,bif_clashes, 39 transforms,maps_warnings,bad_utf8,bad_decls]}]. 40 41init_per_suite(Config) -> 42 test_lib:recompile(?MODULE), 43 Config. 44 45end_per_suite(_Config) -> 46 ok. 47 48init_per_group(_GroupName, Config) -> 49 Config. 50 51end_per_group(_GroupName, Config) -> 52 Config. 53 54 55bif_clashes(Config) when is_list(Config) -> 56 Ts = [{bif_clashes1, 57 <<" 58 -export([t/0]). 59 t() -> 60 length([a,b,c]). 61 62 length(X) -> 63 erlang:length(X). 64 ">>, 65 [return_warnings], 66 {error, 67 [{{4,18}, erl_lint,{call_to_redefined_old_bif,{length,1}}}], []} }], 68 [] = run(Config, Ts), 69 Ts1 = [{bif_clashes2, 70 <<" 71 -export([t/0]). 72 -import(x,[length/1]). 73 t() -> 74 length([a,b,c]). 75 ">>, 76 [return_warnings], 77 {error, 78 [{{3,16}, erl_lint,{redefine_old_bif_import,{length,1}}}], []} }], 79 [] = run(Config, Ts1), 80 Ts00 = [{bif_clashes3, 81 <<" 82 -export([t/0]). 83 -compile({no_auto_import,[length/1]}). 84 t() -> 85 length([a,b,c]). 86 87 length(X) -> 88 erlang:length(X). 89 ">>, 90 [return_warnings], 91 []}], 92 [] = run(Config, Ts00), 93 Ts11 = [{bif_clashes4, 94 <<" 95 -export([t/0]). 96 -compile({no_auto_import,[length/1]}). 97 -import(x,[length/1]). 98 t() -> 99 length([a,b,c]). 100 ">>, 101 [return_warnings], 102 []}], 103 [] = run(Config, Ts11), 104 Ts000 = [{bif_clashes5, 105 <<" 106 -export([t/0]). 107 t() -> 108 binary_part(<<1,2,3,4>>,1,2). 109 110 binary_part(X,Y,Z) -> 111 erlang:binary_part(X,Y,Z). 112 ">>, 113 [return_warnings], 114 {warning, 115 [{{4,18}, erl_lint,{call_to_redefined_bif,{binary_part,3}}}]} }], 116 [] = run(Config, Ts000), 117 Ts111 = [{bif_clashes6, 118 <<" 119 -export([t/0]). 120 -import(x,[binary_part/3]). 121 t() -> 122 binary_part(<<1,2,3,4>>,1,2). 123 ">>, 124 [return_warnings], 125 {warning, 126 [{{3,16}, erl_lint,{redefine_bif_import,{binary_part,3}}}]} }], 127 [] = run(Config, Ts111), 128 Ts2 = [{bif_clashes7, 129 <<" 130 -export([t/0]). 131 -compile({no_auto_import,[length/1]}). 132 -import(x,[length/1]). 133 t() -> 134 length([a,b,c]). 135 length(X) -> 136 erlang:length(X). 137 ">>, 138 [], 139 {error, 140 [{{7,15},erl_lint,{define_import,{length,1}}}], 141 []} }], 142 [] = run2(Config, Ts2), 143 Ts3 = [{bif_clashes8, 144 <<" 145 -export([t/1]). 146 -compile({no_auto_import,[length/1]}). 147 t(X) when length(X) > 3 -> 148 length([a,b,c]). 149 length(X) -> 150 erlang:length(X). 151 ">>, 152 [], 153 {error, 154 [{{4,25},erl_lint,{illegal_guard_local_call,{length,1}}}], 155 []} }], 156 [] = run2(Config, Ts3), 157 Ts4 = [{bif_clashes9, 158 <<" 159 -export([t/1]). 160 -compile({no_auto_import,[length/1]}). 161 -import(x,[length/1]). 162 t(X) when length(X) > 3 -> 163 length([a,b,c]). 164 ">>, 165 [], 166 {error, 167 [{{5,25},erl_lint,{illegal_guard_local_call,{length,1}}}], 168 []} }], 169 [] = run2(Config, Ts4), 170 171 ok. 172 173 174 175 176%% Tests that a head mismatch is reported on the correct line (OTP-2125). 177head_mismatch_line(Config) when is_list(Config) -> 178 [E|_] = get_compilation_errors(Config, "head_mismatch_line"), 179 {{26,1}, Mod, Reason} = E, 180 Mod:format_error(Reason), 181 ok. 182 183%% Compiles a test file and returns the list of errors. 184 185get_compilation_errors(Config, Filename) -> 186 DataDir = proplists:get_value(data_dir, Config), 187 File = filename:join(DataDir, Filename), 188 {error, [{_Name, E}|_], []} = compile:file(File, [return_errors]), 189 E. 190 191warnings_as_errors(Config) when is_list(Config) -> 192 TestFile = test_filename(Config), 193 BeamFile = filename:rootname(TestFile, ".erl") ++ ".beam", 194 OutDir = proplists:get_value(priv_dir, Config), 195 196 Ts1 = [{warnings_as_errors, 197 <<" 198 t() -> 199 A = unused, 200 ok. 201 ">>, 202 [warnings_as_errors, export_all, {outdir, OutDir}], 203 {error, 204 [], 205 [{{3,18},erl_lint,{unused_var,'A'}}]} }], 206 [] = run(Ts1, TestFile, write_beam), 207 false = filelib:is_regular(BeamFile), 208 209 Ts2 = [{warning_unused_var, 210 <<" 211 t() -> 212 A = unused, 213 ok. 214 ">>, 215 [return_warnings, export_all, {outdir, OutDir}], 216 {warning, 217 [{{3,18},erl_lint,{unused_var,'A'}}]} }], 218 219 [] = run(Ts2, TestFile, write_beam), 220 true = filelib:is_regular(BeamFile), 221 ok = file:delete(BeamFile), 222 223 ok. 224 225transforms(Config) -> 226 Ts1 = [{undef_parse_transform, 227 <<" 228 -compile({parse_transform,non_existing}). 229 ">>, 230 [return], 231 {error,[{none,compile,{undef_parse_transform,non_existing}}],[]}}], 232 [] = run(Config, Ts1), 233 234 Ts2 = <<" 235 -compile({parse_transform,",?MODULE_STRING,"}). 236 ">>, 237 238 {error,[{none,compile,{parse_transform,?MODULE,{error,too_bad,_}}}],[]} = 239 run_test(Ts2, test_filename(Config), [{pt_error,error}], dont_write_beam), 240 241 {error,[{none,compile,{parse_transform,?MODULE,{error,undef,_}}}],[]} = 242 run_test(Ts2, test_filename(Config), [{pt_error,call_undef}], dont_write_beam), 243 244 {error,[{none,compile,{parse_transform,?MODULE,{exit,exited,_}}}],[]} = 245 run_test(Ts2, test_filename(Config), [{pt_error,exit}], dont_write_beam), 246 247 {error,[{none,compile,{parse_transform,?MODULE,{throw,thrown,[_|_]}}}],[]} = 248 run_test(Ts2, test_filename(Config), [{pt_error,throw}], dont_write_beam), 249 250 ok. 251 252parse_transform(_, Opts) -> 253 {_,Error} = lists:keyfind(pt_error, 1, Opts), 254 case Error of 255 call_undef -> 256 camembert:délicieux(); 257 throw -> 258 throw(thrown); 259 exit -> 260 exit(exited); 261 error -> 262 error(too_bad) 263 end. 264 265maps_warnings(Config) when is_list(Config) -> 266 Ts1 = [{map_ok_use_of_pattern, 267 <<" 268 -export([t/1]). 269 t(K) -> 270 #{K := 1 = V} = id(#{<<\"hi all\">> => 1}), 271 V. 272 id(I) -> I. 273 ">>, 274 [return], 275 []}, 276 {map_illegal_use_of_pattern, 277 <<" 278 -export([t/0,t/2]). 279 t(K,#{ K := V }) -> V. 280 t() -> 281 V = 32, 282 #{<<\"hi\",V,\"all\">> := 1} = id(#{<<\"hi all\">> => 1}). 283 id(I) -> I. 284 ">>, 285 [return], 286 {error,[{{3,15},erl_lint,{unbound_var,'K'}}],[]}} 287 ], 288 [] = run2(Config, Ts1), 289 ok. 290 291bad_utf8(Config) -> 292 Ts = [{bad_explicit_utf8, 293 %% If coding is specified explicitly as utf-8, there should be 294 %% a compilation error for a latin-1 comment. 295 <<"%% coding: utf-8 296 %% Bj",246,"rn 297 t() -> \"",246,"\". 298 ">>, 299 [], 300 {error,[{{2,15},epp,cannot_parse}, 301 {{2,15},file_io_server,invalid_unicode}], 302 []} 303 }, 304 305 {bad_implicit_utf8, 306 %% If there is no coding comment given, encoding defaults to utf-8 307 %% and there should be a compilation error for a latin-1 comment. 308 <<" 309 %% Bj",246,"rn 310 t() -> \"",246,"\". 311 ">>, 312 [], 313 {error,[{{2,15},epp,cannot_parse}, 314 {{2,15},file_io_server,invalid_unicode}], 315 []} 316 } 317 ], 318 [] = run2(Config, Ts), 319 ok. 320 321bad_decls(Config) -> 322 Ts = [{bad_decls_1, 323 <<"\n-module({l}). 324 ">>, 325 [], 326 {error,[{{2,9},erl_parse,"bad " ++ ["module"] ++ " declaration"}], 327 []} 328 }, 329 {bad_decls_2, 330 <<"\n-module(l, m). 331 ">>, 332 [], 333 {error,[{{2,12},erl_parse,"bad variable list"}],[]} 334 }, 335 {bad_decls_3, 336 <<"\n-export([a/1], Y). 337 ">>, 338 [], 339 {error,[{{2,16},erl_parse,"bad " ++ ["export"] ++ " declaration"}], 340 []} 341 }, 342 {bad_decls_4, 343 <<"\n-import([a/1], Y). 344 ">>, 345 [], 346 {error,[{{2,16},erl_parse,"bad " ++ ["import"] ++ " declaration"}], 347 []} 348 }, 349 {bad_decls_5, 350 <<"\n-ugly({A,B}). 351 ">>, 352 [], 353 {error,[{{2,7},erl_parse,"bad attribute"}],[]} 354 }, 355 {bad_decls_6, 356 <<"\n-ugly(a, b). 357 ">>, 358 [], 359 {error,[{{2,10},erl_parse,"bad attribute"}],[]} 360 }, 361 {bad_decls_7, 362 <<"\n-export([A/1]). 363 ">>, 364 [], 365 {error,[{{2,10},erl_parse,"bad function name"}],[]} 366 }, 367 {bad_decls_8, 368 <<"\n-export([a/a]). 369 ">>, 370 [], 371 {error,[{{2,12},erl_parse,"bad function arity"}],[]} 372 }, 373 {bad_decls_9, 374 <<"\n-export([a/1, {3,4}]). 375 ">>, 376 [], 377 {error,[{{2,15},erl_parse,"bad Name/Arity"}],[]} 378 }, 379 {bad_decls_10, 380 <<"\n-record(A, {{bad,a}}). 381 ">>, 382 [], 383 {error,[{{2,9},erl_parse,"bad " ++ ["record"] ++ " declaration"}], 384 []} 385 }, 386 {bad_decls_11, 387 <<"\n-record(a, [a,b,c,d]). 388 ">>, 389 [], 390 {error,[{{2,12},erl_parse,"bad record declaration"}],[]} 391 }, 392 {bad_decls_12, 393 <<"\n-record(a). 394 ">>, 395 [], 396 {error,[{{2,9},erl_parse,"bad " ++ ["record"] ++ " declaration"}], 397 []} 398 } 399 ], 400 [] = run2(Config, Ts), 401 402 {error,{{1,4},erl_parse,"bad term"}} = parse_string("1, 2 + 4."), 403 {error,{{1,1},erl_parse,"bad term"}} = parse_string("34 + begin 34 end."), 404 ok. 405 406parse_string(S) -> 407 {ok,Ts,_} = erl_scan:string(S, {1, 1}), 408 erl_parse:parse_term(Ts). 409 410 411run(Config, Tests) -> 412 File = test_filename(Config), 413 run(Tests, File, dont_write_beam). 414 415run(Tests, File, WriteBeam) -> 416 F = fun({N,P,Ws,E}, BadL) -> 417 case catch run_test(P, File, Ws, WriteBeam) of 418 E -> 419 BadL; 420 Bad -> 421 io:format("~nTest ~p failed. Expected~n ~p~n" 422 "but got~n ~p~n", [N, E, Bad]), 423 fail() 424 end 425 end, 426 lists:foldl(F, [], Tests). 427 428run2(Config, Tests) -> 429 File = test_filename(Config), 430 run2(Tests, File, dont_write_beam). 431 432run2(Tests, File, WriteBeam) -> 433 F = fun({N,P,Ws,E}, BadL) -> 434 case catch filter(run_test(P, File, Ws, WriteBeam)) of 435 E -> 436 BadL; 437 Bad -> 438 io:format("~nTest ~p failed. Expected~n ~p~n" 439 "but got~n ~p~n", [N, E, Bad]), 440 fail() 441 end 442 end, 443 lists:foldl(F, [], Tests). 444 445filter({error,Es,_Ws}) -> 446 {error,Es,[]}; 447filter(X) -> 448 X. 449 450 451%% Compiles a test module and returns the list of errors and warnings. 452 453test_filename(Conf) -> 454 Filename = ["errors_test_",test_lib:uniq(),".erl"], 455 DataDir = proplists:get_value(priv_dir, Conf), 456 filename:join(DataDir, Filename). 457 458run_test(Test0, File, Warnings, WriteBeam) -> 459 ModName = filename:rootname(filename:basename(File), ".erl"), 460 Mod = list_to_atom(ModName), 461 Test = iolist_to_binary(["-module(",ModName,"). ",Test0]), 462 Opts = case WriteBeam of 463 dont_write_beam -> 464 [binary,return_errors|Warnings]; 465 write_beam -> 466 [return_errors|Warnings] 467 end, 468 ok = file:write_file(File, Test), 469 470 %% Compile once just to print all errors and warnings. 471 compile:file(File, [binary,report|Warnings]), 472 473 %% Test result of compilation. 474 io:format("~p\n", [Opts]), 475 Res = case compile:file(File, Opts) of 476 {ok,Mod,_,[{_File,Ws}]} -> 477 print_diagnostics(Ws, Test), 478 {warning,Ws}; 479 {ok,Mod,_,[]} -> 480 []; 481 {ok,Mod,[{_File,Ws}]} -> 482 {warning,Ws}; 483 {ok,Mod,[]} -> 484 []; 485 {error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) -> 486 print_diagnostics(Es, Test), 487 {error,Es,Ws}; 488 {error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ 489 when is_list(XFile) -> 490 Es = Es1 ++ Es2, 491 print_diagnostics(Es, Test), 492 {error,Es,Ws}; 493 {error,Es,[{_File,Ws}]} = _ZZ-> 494 print_diagnostics(Es ++ Ws, Test), 495 {error,Es,Ws} 496 end, 497 file:delete(File), 498 Res. 499 500print_diagnostics(Warnings, Source) -> 501 case binary:match(Source, <<"-file(">>) of 502 nomatch -> 503 Lines = binary:split(Source, <<"\n">>, [global]), 504 Cs = [print_diagnostic(W, Lines) || W <- Warnings], 505 io:put_chars(Cs); 506 _ -> 507 %% There are probably fake line numbers greater than 508 %% the number of actual lines. 509 ok 510 end. 511 512print_diagnostic({{LineNum,Column},Mod,Data}, Lines) -> 513 Line0 = lists:nth(LineNum, Lines), 514 <<Line1:(Column-1)/binary,_/binary>> = Line0, 515 Spaces = re:replace(Line1, <<"[^\t]">>, <<" ">>, [global]), 516 CaretLine = [Spaces,"^"], 517 [io_lib:format("~p:~p: ~ts\n", [LineNum,Column,Mod:format_error(Data)]), 518 Line0, "\n", 519 CaretLine, "\n\n"]; 520print_diagnostic(_, _) -> 521 []. 522 523fail() -> 524 ct:fail(failed). 525