1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2006-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: %%%---------------------------------------------------------------- 20: %%% Purpose:Test Suite for the 'erl_pp' module. 21: %%%----------------------------------------------------------------- 22: -module(erl_pp_SUITE). 23: 24: %-define(debug, true). 25: 26: -ifdef(debug). 27: -define(line, put(line, ?LINE), ). 28: -define(config(X,Y), foo). 29: -define(datadir, "erl_pp_SUITE_data"). 30: -define(privdir, "erl_pp_SUITE_priv"). 31: -define(t, test_server). 32: -else. 33: -include_lib("test_server/include/test_server.hrl"). 34: -define(datadir, ?config(data_dir, Config)). 35: -define(privdir, ?config(priv_dir, Config)). 36: -endif. 37: 38: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 39: init_per_group/2,end_per_group/2, 40: init_per_testcase/2, end_per_testcase/2]). 41: 42: -export([ func/1, call/1, recs/1, try_catch/1, if_then/1, 43: receive_after/1, bits/1, head_tail/1, 44: cond1/1, block/1, case1/1, ops/1, messages/1, 45: old_mnemosyne_syntax/1, 46: import_export/1, misc_attrs/1, dialyzer_attrs/1, 47: hook/1, 48: neg_indent/1, 49: 50: otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, 51: otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, 52: otp_10302/1, otp_10820/1, otp_11100/1]). 53: 54: %% Internal export. 55: -export([ehook/6]). 56: 57: % Default timetrap timeout (set in init_per_testcase). 58: -define(default_timeout, ?t:minutes(2)). 59: 60: init_per_testcase(_Case, Config) -> 61: ?line Dog = ?t:timetrap(?default_timeout), 62: [{watchdog, Dog} | Config]. 63: 64: end_per_testcase(_Case, _Config) -> 65: Dog = ?config(watchdog, _Config), 66: test_server:timetrap_cancel(Dog), 67: ok. 68: 69: suite() -> [{ct_hooks,[ts_install_cth]}]. 70: 71: all() -> 72: [{group, expr}, {group, attributes}, hook, neg_indent, 73: {group, tickets}]. 74: 75: groups() -> 76: [{expr, [], 77: [func, call, recs, try_catch, if_then, receive_after, 78: bits, head_tail, cond1, block, case1, ops, 79: messages, old_mnemosyne_syntax]}, 80: {attributes, [], [misc_attrs, import_export, dialyzer_attrs]}, 81: {tickets, [], 82: [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, 83: otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, 84: otp_10302, otp_10820, otp_11100]}]. 85: 86: init_per_suite(Config) -> 87: Config. 88: 89: end_per_suite(_Config) -> 90: ok. 91: 92: init_per_group(_GroupName, Config) -> 93: Config. 94: 95: end_per_group(_GroupName, Config) -> 96: Config. 97: 98: 99: 100: func(suite) -> 101: []; 102: func(Config) when is_list(Config) -> 103: Ts = [{func_1, 104: <<"-record(r1, {a,b}). 105: -record(r3, {a = fun(_) -> #r1{} end(1), b}). 106: 107: t() -> 108: fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}). 109: ">>}, 110: {func_2, 111: <<"-record(r1, {a,b}). 112: -record(r3, {a = fun(_) -> #r1{} end(1), b}). 113: 114: t() -> 115: fsdfsdfjsdfjkljf:sdlfjdsfjlf( 116: fun(sdfsd) -> {sdkjsdf,sdfjsdkljfsdl,sdfkjdklf} end). 117: ">>}, 118: {func_3, 119: <<"t() -> fun t/0.">>}, 120: {func_4, 121: <<"t() -> fun modul:foo/3.">>}, 122: {func_5, % 'when' is moved down one line 123: <<"tkjlksjflksdjflsdjlk() 124: when kljlsajflksjdfklsjdlkjfklsdklfjsdlf < 125: kljasjfdsjflsdjfklsdjfklsdjfklsd -> 126: foo.">>}, 127: {func_6, 128: <<"t() -> 129: (fun() -> 130: true 131: end)().">>}, 132: {func_7, 133: <<"t(M, F, A) -> fun M:F/A.">>} 134: ], 135: ?line compile(Config, Ts), 136: ok. 137: 138: call(suite) -> 139: []; 140: call(Config) when is_list(Config) -> 141: Ts = [{call_1, 142: <<"t() -> 143: fookjljsflj:barlkjlkjsdfj(kjdslfjsdl,hej,san,sa, 144: foo,sdfds,sdfsdf,sdfsd,sdfdsf,sdfdsf,sfdsf, 145: sfds,sdfsdf,sfds). 146: ">>} 147: ], 148: ?line compile(Config, Ts), 149: ok. 150: 151: recs(suite) -> 152: []; 153: recs(Config) when is_list(Config) -> 154: %% Evolved while testing strict record tests in guards... 155: Ts = [{recs_1, 156: <<"-compile(strict_record_tests). 157: -record(r, {a = 4,b}). 158: -record(r1, {a,b}). 159: -record(r2, {a = #r1{},b,c=length([1,2,3])}). 160: -record(r3, {a = fun(_) -> #r1{} end(1), b}). 161: 162: t() -> 163: foo = fun(A) when A#r1.a > A#r1.b -> foo end(#r1{b = 2}), 164: 0 = fun(A) when A#r2.a -> 0 end(#r2{a = true}), 165: 1 = fun(A) when (#r1{a = A})#r1.a > 2 -> 1 end(3), 166: 2 = fun(N) when ((#r2{a = #r{a = 4}, b = length([a,b,c])})#r2.a)#r.a > N -> 167: 2 end(2), 168: 3 = fun(A) when (A#r2.a)#r1.a =:= 3 -> 3 end(#r2{a = #r1{a = 3}}), 169: ok = fun() -> 170: F = fun(A) when record(A#r.a, r1) -> 4; 171: (A) when record(A#r1.a, r1) -> 5 172: end, 173: 5 = F(#r1{a = #r1{}}), 174: 4 = F(#r{a = #r1{}}), 175: ok 176: end(), 177: 3 = fun(A) when record(A#r1.a, r), 178: (A#r1.a)#r.a > 3 -> 3 179: end(#r1{a = #r{a = 4}}), 180: 7 = fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}), 181: [#r1{a = 2,b = 1}] = 182: fun() -> 183: [A || A <- [#r1{a = 1, b = 3}, 184: #r2{a = 2,b = 1}, 185: #r1{a = 2, b = 1}], 186: A#r1.a > 187: A#r1.b] 188: end(), 189: {[_],b} = 190: fun(L) -> 191: %% A is checked only once: 192: R1 = [{A,B} || A <- L, A#r1.a, B <- L, A#r1.b], 193: A = #r2{a = true}, 194: %% A is checked again: 195: B = if A#r1.a -> a; true -> b end, 196: {R1,B} 197: end([#r1{a = true, b = true}]), 198: 199: p = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; 200: (_) -> p 201: end(#r1{a = 2}), 202: 203: o = fun(A) when (A#r1.a =:= 2) orelse (A#r2.a =:= 1) -> o; 204: (_) -> p 205: end(#r1{a = 2}), 206: 207: %% The test done twice (an effect of doing the test as soon as possible). 208: 3 = fun(A) when A#r1.a > 3, 209: record(A, r1) -> 3 210: end(#r1{a = 5}), 211: 212: ok = fun() -> 213: F = fun(A) when (A#r2.a =:= 1) orelse (A#r2.a) -> 2; 214: (A) when (A#r1.a =:= 1) orelse (A#r1.a) -> 1; 215: (A) when (A#r2.a =:= 2) andalso (A#r2.b) -> 3 216: end, 217: 1 = F(#r1{a = 1}), 218: 2 = F(#r2{a = true}), 219: 3 = F(#r2{a = 2, b = true}), 220: ok 221: end(), 222: 223: b = fun(A) when false or not (A#r.a =:= 1) -> a; 224: (_) -> b 225: end(#r1{a = 1}), 226: b = fun(A) when not (A#r.a =:= 1) or false -> a; 227: (_) -> b 228: end(#r1{a = 1}), 229: 230: ok = fun() -> 231: F = fun(A) when not (A#r.a =:= 1) -> yes; 232: (_) -> no 233: end, 234: no = F(#r1{a = 2}), 235: yes = F(#r{a = 2}), 236: no = F(#r{a = 1}), 237: ok 238: end(), 239: 240: %% No extra check added: 241: a = fun(A) when record(A, r), 242: A#r.a =:= 1, 243: A#r.b =:= 2 ->a 244: end(#r{a = 1, b = 2}), 245: a = fun(A) when erlang:is_record(A, r), 246: A#r.a =:= 1, 247: A#r.b =:= 2 -> a 248: end(#r{a = 1, b = 2}), 249: a = fun(A) when is_record(A, r), 250: A#r.a =:= 1, 251: A#r.b =:= 2 -> a 252: end(#r{a = 1, b = 2}), 253: 254: nop = fun(A) when (is_record(A, r1) and (A#r1.a > 3)) or (A#r2.a < 1) -> 255: japp; 256: (_) -> 257: nop 258: end(#r2{a = 0}), 259: nop = fun(A) when (A#r1.a > 3) or (A#r2.a < 1) -> japp; 260: (_) -> 261: nop 262: end(#r2{a = 0}), 263: 264: ok = fun() -> 265: F = fun(A) when (A#r1.a =:= 2) or (A#r2.a =:= 1) -> o; 266: (_) -> p 267: end, 268: p = F(#r2{a = 1}), 269: p = F(#r1{a = 2}), 270: ok 271: end(), 272: 273: ok = fun() -> 274: F = fun(A) when fail, A#r1.a; A#r1.a -> ab; 275: (_) -> bu 276: end, 277: ab = F(#r1{a = true}), 278: bu = F(#r2{a = true}), 279: ok 280: end(), 281: 282: both = fun(A) when A#r.a, A#r.b -> both 283: end(#r{a = true, b = true}), 284: 285: ok = fun() -> 286: F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) 287: or (B#r2.b) or (A#r1.b) -> 288: true; 289: (_, _) -> false 290: end, 291: true = F(#r1{a = false, b = false}, 292: #r2{a = false, b = true}), 293: false = F(#r1{a = true, b = true}, 294: #r1{a = false, b = true}), 295: ok 296: end(), 297: 298: ok. 299: ">>}, 300: {recs_2, 301: <<"-record(r1, {a, b = foo:bar(kljlfjsdlf, kjlksdjf)}). 302: -record(r2, {c = #r1{}, d = #r1{a = bar:foo(kljklsjdf)}}). 303: 304: t() -> 305: R = #r2{}, 306: R#r2{c = R, d = #r1{}}.">>} 307: ], 308: ?line compile(Config, Ts), 309: 310: ?line ok = pp_expr(<<"case #r{a={1,2},b=#r{}} of 311: X=Y=#r{a=foo,b=bar} -> 312: {(foooo:baaaar(X))#r{a = rep},Y,#r.b} 313: end">>), 314: ?line ok = pp_expr(<<"R#r{a = {kljasdklf,sdkfjsdl,sdafjkllsdf,sdfkjsd, 315: sdafjsd,sdf,sdafsd,sdfdsf,sdfdsf,dsfds}}">>), 316: ok. 317: 318: try_catch(suite) -> 319: []; 320: try_catch(Config) when is_list(Config) -> 321: Ts = [{try_1, % copied from erl_eval_SUITE 322: <<"t() -> try 1 of 1 -> 2 catch _:_ -> 3 end.">>}, 323: {try_2, 324: <<"t() -> try 1 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end.">>}, 325: {try_3, 326: <<"t() -> try 3 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end.">>}, 327: {try_4, 328: <<"t() -> try 1 after put(try_catch, 2) end.">>}, 329: {try_5, 330: <<"t() -> try 1 of 1 -> 2; 3 -> 4 331: after put(try_catch, 5) end.">>}, 332: {try_6, 333: <<"t() -> try 1=2 catch throw:{badmatch,2} -> 3 end.">>}, 334: {try_7, 335: <<"t() -> try 1=2 of 3 -> 4 336: catch error:{badmatch,2} -> 5 end.">>}, 337: {try_8, 338: <<"t() -> try 1=2 339: catch error:{badmatch,2} -> 3 340: after put(try_catch, 4) end.">>}, 341: {try_9, 342: <<"t() -> try 1=2 343: catch error:{badmatch,2} -> 3 344: after put(try_catch, 4) end.">>}, 345: {try_10, 346: <<"t() -> try a,b,c 347: catch exit:_ -> d; 348: throw:_ -> t; 349: error:{foo,bar} -> foo, 350: bar 351: end.">>}, 352: 353: {catch_1, 354: <<"t() -> catch foo.">>}, 355: {catch_2, 356: <<"t() -> case catch foo of bar -> foo end.">>}, 357: {catch_3, 358: <<"t() -> catch begin begin foo, bar, foo:bar(kljsldkfjdls,kljsdl), 359: (catch bar:foo(foo)) end end.">>} 360: ], 361: ?line compile(Config, Ts), 362: ?line ok = pp_expr(<<"try 363: erl_internal:bif(M,F,length(Args)) 364: of 365: true -> 366: call(N,Args,Prec,Hook); 367: false -> 368: call(Name,Args,Prec,Hook) 369: after foo end">>), 370: ok. 371: 372: if_then(suite) -> 373: []; 374: if_then(Config) when is_list(Config) -> 375: Ts = [{if_1, 376: <<"t() -> if 1 > 2 -> 1; true -> b end.">>}, 377: {if_2, 378: <<"t() -> if true -> true end.">>}, 379: {if_3, 380: <<"t() -> if 1 == 2 -> a; 1 > 2 -> b; 1 < 2 -> c end.">>} 381: ], 382: ?line compile(Config, Ts), 383: ok. 384: 385: receive_after(suite) -> 386: []; 387: receive_after(Config) when is_list(Config) -> 388: Ts = [{rec_1, 389: <<"t() -> receive foo -> bar; bar -> foo end.">>}, 390: {rec_2, 391: <<"t() -> receive foo -> bar after foo:bar() -> 0 end.">>}, 392: {rec_3, 393: <<"t() -> receive after 1 -> ok end.">>}, 394: {rec_4, 395: <<"t() -> receive {X,Y} -> {a,X,Y} end.">>}, 396: {rec_5, 397: <<"t() -> receive 398: {X,Y} -> 399: {X,Y}; 400: Z -> 401: Z 402: after 403: foo:bar() -> 404: {3,4} 405: end.">>} 406: ], 407: ?line compile(Config, Ts), 408: ok. 409: 410: bits(suite) -> 411: []; 412: bits(Config) when is_list(Config) -> 413: Ts = [{bit_1, % copied from shell_SUITE 414: <<"t() -> <<(<<\"abc\">>):3/binary>>.">>}, 415: {bit_2, 416: <<"t() -> <<(<<\"abc\">>)/binary>>.">>}, 417: {bit_3, 418: <<"t() -> <<3.14:64/float>>.">>}, 419: {bit_4, 420: <<"t() -> <<-20/signed>> = <<-20>>.">>}, 421: {bit_5, 422: <<"t() -> <<-300:16/signed>> = <<-300:16>>.">>}, 423: {bit_6, 424: <<"t() -> <<-(1 bsl 29):32/signed>> = <<-(1 bsl 29):32>>.">>}, 425: {bit_7, 426: <<"t() -> <<A:4,B:4,C:4,D:4,E:4,F:4>> = <<\"hej\">>.">>}, 427: {bit_8, 428: <<"t() -> <<>>.">>}, 429: {bit_9, 430: <<"">>} 431: ], 432: ?line compile(Config, Ts), 433: ?line ok = pp_expr(<<"<<(list_to_binary([1,2]))/binary>>">>), 434: ?line ok = pp_expr( 435: <<"<<(list_to_binary([1,2])):all/binary-unit:8-unsigned-big>>">>), 436: ?line ok = pp_expr(<<"<<<<\"hej\">>/binary>>">>), 437: ?line ok = pp_expr(<<"<<(foo:bar())/binary>>">>), 438: ?line ok = pp_expr(<<"<<(a)/binary>>">>), 439: ?line ok = pp_expr(<<"<<a/binary>>">>), 440: ?line ok = pp_expr(<<"<<{a,b}/binary>>">>), 441: ?line ok = pp_expr(<<"<<{foo:bar(),b}/binary>>">>), 442: ?line ok = pp_expr(<<"<<(foo:bar()):(foo:bar())/binary>>">>), 443: ok. 444: 445: head_tail(suite) -> 446: []; 447: head_tail(Config) when is_list(Config) -> 448: Ts = [{list_1, 449: <<"t() -> [a | b].">>}, 450: {list_2, 451: <<"t() -> [a,b,$\n].">>}, 452: {list_3, 453: <<"t() -> [].">>}, 454: {list_4, 455: <<"t() -> [a].">>}, 456: {list_5, 457: <<"t() -> 458: [foo:bar(lkjljlskdfj, klsdajflds, sdafkljsdlfkjdas, kjlsdadjl), 459: bar:foo(kljlkjsdf, lkjsdlfj, [kljsfj, sdfdsfsad])].">>} 460: ], 461: ?line compile(Config, Ts), 462: ok. 463: 464: cond1(suite) -> 465: []; 466: cond1(Config) when is_list(Config) -> 467: C = {'cond',1,[{clause,2,[],[[{tuple,2,[{atom,2,foo},{atom,2,bar}]}]], 468: [{cons,3,{atom,3,a},{cons,3,{atom,3,b},{nil,3}}}]}, 469: {clause,4,[],[[{atom,4,true}]], 470: [{tuple,5,[{atom,5,x},{atom,5,y}]}]}]}, 471: ?line CChars = lists:flatten(erl_pp:expr(C)), 472: % ?line "cond {foo,bar} -> [a,b]; true -> {x,y} end" = CChars, 473: ?line "cond\n" 474: " {foo,bar} ->\n" 475: " [a,b];\n" 476: " true ->\n" 477: " {x,y}\n" 478: "end" = CChars, 479: % ?line ok = pp_expr(<<"cond 480: % {foo,bar} -> 481: % [a,b]; 482: % true -> 483: % {x,y} 484: % end">>), 485: ok. 486: 487: block(suite) -> 488: []; 489: block(Config) when is_list(Config) -> 490: Ts = [{block_1, 491: <<"t() -> begin a,{c,d} end.">>} 492: ], 493: ?line compile(Config, Ts), 494: ok. 495: 496: case1(suite) -> 497: []; 498: case1(Config) when is_list(Config) -> 499: Ts = [{case_1, 500: <<"t() -> case {foo,bar} of 501: {A,B} when true -> 502: [A,B]; 503: _ -> 504: foo 505: end.">>} 506: ], 507: ?line compile(Config, Ts), 508: ?line ok = pp_expr(<<"case 509: erl_internal:bif(M,F,length(Args)) 510: of 511: true -> 512: call(N,Args,Prec,Hook); 513: false -> 514: call(Name,Args,Prec,Hook) 515: end">>), 516: ok. 517: 518: ops(suite) -> 519: []; 520: ops(Config) when is_list(Config) -> 521: Ts = [{ops_1, 522: <<"t() -> {a,b} + (3 - 2) + 4.">>}, 523: {ops_2, 524: <<"t() -> a - (3 + 4).">>}, 525: {ops_3, 526: <<"t() -> - (- (- (- (- 3)))).">>} 527: ], 528: ?line compile(Config, Ts), 529: ok. 530: 531: messages(suite) -> 532: []; 533: messages(Config) when is_list(Config) -> 534: ?line true = "{error,{some,\"error\"}}\n" =:= 535: lists:flatten(erl_pp:form({error,{some,"error"}})), 536: ?line true = "{warning,{some,\"warning\"}}\n" =:= 537: lists:flatten(erl_pp:form({warning,{some,"warning"}})), 538: ?line true = "\n" =:= lists:flatten(erl_pp:form({eof,0})), 539: ok. 540: 541: old_mnemosyne_syntax(Config) when is_list(Config) -> 542: %% Since we have kept the ':-' token, 543: %% better test that we can pretty print it. 544: R = {rule,12,sales,2, 545: [{clause,12, 546: [{var,12,'E'},{atom,12,employee}], 547: [], 548: [{generate,13, 549: {var,13,'E'}, 550: {call,13,{atom,13,table},[{atom,13,employee}]}}, 551: {match,14, 552: {record_field,14,{var,14,'E'},{atom,14,salary}}, 553: {atom,14,sales}}]}]}, 554: ?line "sales(E, employee) :-\n" 555: " E <- table(employee),\n" 556: " E.salary = sales.\n" = 557: lists:flatten(erl_pp:form(R)), 558: ok. 559: 560: 561: 562: import_export(suite) -> 563: []; 564: import_export(Config) when is_list(Config) -> 565: Ts = [{import_1, 566: <<"-import(lists, [max/1, reverse/1]). 567: -import(sofs, []). 568: t(L) -> 569: max(reverse(L)).">>}, 570: {export_1, 571: <<"-export([t/0]). 572: -export([]). 573: t() -> [].">>}, 574: {qlc_1, 575: <<"-include_lib(\"stdlib/include/qlc.hrl\"). 576: t() -> qlc:q([X || X <- []]).">>} 577: ], 578: ?line compile(Config, Ts), 579: ok. 580: 581: misc_attrs(suite) -> 582: []; 583: misc_attrs(Config) when is_list(Config) -> 584: ?line ok = pp_forms(<<"-module(m). ">>), 585: ?line ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk," 586: "Blsjfdlslfjsdf]). ">>), 587: ?line ok = pp_forms(<<"-export([]). ">>), 588: ?line ok = pp_forms(<<"-export([foo/2, bar/0]). ">>), 589: ?line ok = pp_forms(<<"-export([bar/0]). ">>), 590: ?line ok = pp_forms(<<"-import(lists, []). ">>), 591: ?line ok = pp_forms(<<"-import(lists, [map/2]). ">>), 592: ?line ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>), 593: ?line ok = pp_forms(<<"-'wild '({attr2,3}). ">>), 594: ?line ok = pp_forms(<<"-record(a, {b,c}). ">>), 595: ?line ok = pp_forms(<<"-record(' a ', {}). ">>), 596: ?line ok = pp_forms(<<"-record(' a ', {foo = foo:bar()}). ">>), 597: 598: ok. 599: 600: dialyzer_attrs(suite) -> 601: []; 602: dialyzer_attrs(Config) when is_list(Config) -> 603: ok = pp_forms(<<"-type foo() :: #bar{}. ">>), 604: ok = pp_forms(<<"-opaque foo() :: {bar, fun((X, [42,...]) -> X)}. ">>), 605: ok = pp_forms(<<"-spec foo(bar(), qux()) -> [T | baz(T)]. ">>), 606: ok = pp_forms(<<"-callback foo(<<_:32,_:_*4>>, T) -> T. ">>), 607: ok. 608: 609: hook(suite) -> 610: []; 611: hook(Config) when is_list(Config) -> 612: F = fun(H) -> H end, 613: do_hook(F). 614: 615: do_hook(HookFun) -> 616: Lc = parse_expr(binary_to_list(<<"[X || X <- [1,2,3]].">>)), 617: H = HookFun(fun hook/4), 618: Expr = {call,0,{atom,0,fff},[{foo,Lc},{foo,Lc},{foo,Lc}]}, 619: EChars = lists:flatten(erl_pp:expr(Expr, 0, H)), 620: Call = {call,0,{atom,0,foo},[Lc]}, 621: Expr2 = {call,0,{atom,0,fff},[Call,Call,Call]}, 622: EChars2 = erl_pp:exprs([Expr2]), 623: ?line true = EChars =:= lists:flatten(EChars2), 624: 625: EsChars = erl_pp:exprs([Expr], H), 626: ?line true = EChars =:= lists:flatten(EsChars), 627: 628: F = {function,1,ffff,0,[{clause,1,[],[],[Expr]}]}, 629: FuncChars = lists:flatten(erl_pp:function(F, H)), 630: F2 = {function,1,ffff,0,[{clause,1,[],[],[Expr2]}]}, 631: FuncChars2 = erl_pp:function(F2), 632: ?line true = FuncChars =:= lists:flatten(FuncChars2), 633: FFormChars = erl_pp:form(F, H), 634: ?line true = FuncChars =:= lists:flatten(FFormChars), 635: 636: A = {attribute,1,record,{r,[{record_field,1,{atom,1,a},Expr}]}}, 637: AChars = lists:flatten(erl_pp:attribute(A, H)), 638: A2 = {attribute,1,record,{r,[{record_field,1,{atom,1,a},Expr2}]}}, 639: AChars2 = erl_pp:attribute(A2), 640: ?line true = AChars =:= lists:flatten(AChars2), 641: AFormChars = erl_pp:form(A, H), 642: ?line true = AChars =:= lists:flatten(AFormChars), 643: 644: R = {rule,0,sales,0, 645: [{clause,0,[{var,0,'E'},{atom,0,employee}],[], 646: [{generate,2,{var,2,'E'}, 647: {call,2,{atom,2,table},[{atom,2,employee}]}}, 648: {match,3, 649: {record_field,3,{var,3,'E'},{atom,3,salary}}, 650: {foo,Expr}}]}]}, 651: RChars = lists:flatten(erl_pp:rule(R, H)), 652: R2 = {rule,0,sales,0, 653: [{clause,0,[{var,0,'E'},{atom,0,employee}],[], 654: [{generate,2,{var,2,'E'}, 655: {call,2,{atom,2,table},[{atom,2,employee}]}}, 656: {match,3, 657: {record_field,3,{var,3,'E'},{atom,3,salary}}, 658: {call,0,{atom,0,foo},[Expr2]}}]}]}, 659: RChars2 = erl_pp:rule(R2), 660: ?line true = RChars =:= lists:flatten(RChars2), 661: ARChars = erl_pp:form(R, H), 662: ?line true = RChars =:= lists:flatten(ARChars), 663: 664: ?line "INVALID-FORM:{foo,bar}:" = lists:flatten(erl_pp:expr({foo,bar})), 665: 666: %% A list (as before R6), not a list of lists. 667: G = [{op,1,'>',{atom,1,a},{foo,{atom,1,b}}}], % not a proper guard 668: GChars = lists:flatten(erl_pp:guard(G, H)), 669: G2 = [{op,1,'>',{atom,1,a}, 670: {call,0,{atom,0,foo},[{atom,1,b}]}}], % not a proper guard 671: GChars2 = erl_pp:guard(G2), 672: ?line true = GChars =:= lists:flatten(GChars2), 673: 674: EH = HookFun({?MODULE, ehook, [foo,bar]}), 675: XEChars = erl_pp:expr(Expr, -1, EH), 676: ?line true = remove_indentation(EChars) =:= lists:flatten(XEChars), 677: XEChars2 = erl_pp:expr(Expr, EH), 678: ?line true = EChars =:= lists:flatten(XEChars2), 679: 680: %% Note: no leading spaces before "begin". 681: Block = {block,0,[{match,0,{var,0,'A'},{integer,0,3}}, 682: {atom,0,true}]}, 683: ?line "begin\n A =" ++ _ = 684: lists:flatten(erl_pp:expr(Block, 17, none)), 685: 686: %% Special... 687: ?line true = 688: "{some,value}" =:= lists:flatten(erl_pp:expr({value,0,{some,value}})), 689: 690: %% Silly... 691: ?line true = 692: "if true -> 0 end" =:= 693: flat_expr({'if',0,[{clause,0,[],[],[{atom,0,0}]}]}), 694: 695: %% More compatibility: before R6 696: OldIf = {'if',0,[{clause,0,[],[{atom,0,true}],[{atom,0,b}]}]}, 697: NewIf = {'if',0,[{clause,0,[],[[{atom,0,true}]],[{atom,0,b}]}]}, 698: OldIfChars = lists:flatten(erl_pp:expr(OldIf)), 699: NewIfChars = lists:flatten(erl_pp:expr(NewIf)), 700: ?line true = OldIfChars =:= NewIfChars, 701: 702: ok. 703: 704: remove_indentation(S) -> 705: %% T is for the very special leaf(" ") used for lc and bc. 706: T = re:replace(S, " \n *", "", [{return,list},global]), 707: re:replace(T, "\n *", " ", [{return,list},global]). 708: 709: ehook(HE, I, P, H, foo, bar) -> 710: hook(HE, I, P, H). 711: 712: hook({foo,E}, I, P, H) -> 713: erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H). 714: 715: neg_indent(suite) -> 716: []; 717: neg_indent(Config) when is_list(Config) -> 718: ?line ok = pp_expr(<<"begin a end">>), 719: ?line ok = pp_expr(<<"begin a,b end">>), 720: ?line ok = pp_expr(<<"try a,b,c 721: catch exit:_ -> d; 722: throw:_ -> t; 723: error:{foo,bar} -> foo, 724: bar 725: end">>), 726: ?line ok = pp_expr( 727: <<"fun() -> 728: F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) 729: or (B#r2.b) or (A#r1.b) -> 730: true; 731: (_, _) -> false 732: end, 733: true = F(#r1{a = false, b = false}, 734: #r2{a = false, b = true}), 735: false = F(#r1{a = true, b = true}, 736: #r1{a = false, b = true}), 737: ok 738: end()">>), 739: 740: ?line ok = pp_expr(<<"[X || X <- a, true]">>), 741: ?line ok = pp_expr(<<"{[a,b,c],[d,e|f]}">>), 742: ?line ok = pp_expr(<<"f(a,b,c)">>), 743: ?line ok = pp_expr(<<"fun() when a,b;c,d -> a end">>), 744: ?line ok = pp_expr(<<"<<34:32,17:32>>">>), 745: ?line ok = pp_expr(<<"if a,b,c -> d; e,f,g -> h,i end">>), 746: ?line ok = pp_expr(<<"if a -> d; c -> d end">>), 747: ?line ok = pp_expr(<<"receive after 1 -> 2 end">>), 748: ?line ok = pp_expr(<<"begin a,b,c end">>), 749: 750: ?line "\"\"" = flat_expr({string,0,""}), 751: ?line ok = pp_expr(<<"\"abc\"">>), 752: ?line ok = pp_expr(<<"\"abc\n\n\n\n\nkjsd\n\n\n\n\nkljsddf\n\n\n\n\n" 753: "klafd\n\n\n\n\nkljsdf\n\n\n\n\nsdf\n\n\n\n\n\"">>), 754: ?line ok = pp_expr(<<"fkjlskljklkkljlkjlkjkljlkjsljklf" 755: "lsdjlfdsjlfjsdlfjdslfjdlsjfsdjfklsdkfjsdf(" 756: "\"abc\n\n\n\n\nkjsd\n\n\n\n\nkljsddf\n\n\n\n\n" 757: "kljsafd\n\n\n\n\nkljsdf\n\n\n\n\nkjsdf" 758: "\n\n\n\n\n\")">>), 759: 760: %% fun-info is skipped when everything is to fit on one single line 761: Fun1 = {'fun',1,{function,t,0},{0,45353021,'-t/0-fun-0-'}}, 762: ?line "fun t/0" = flat_expr(Fun1), 763: Fun2 = {'fun',2,{clauses,[{clause,2,[],[],[{atom,3,true}]}]}, 764: {0,108059557,'-t/0-fun-0-'}}, 765: ?line "fun() -> true end" = flat_expr(Fun2), 766: 767: ok. 768: 769: 770: otp_6321(doc) -> 771: "OTP_6321. Bug fix of exprs()."; 772: otp_6321(suite) -> []; 773: otp_6321(Config) when is_list(Config) -> 774: Str = "S = hopp, {hej, S}. ", 775: {done, {ok, Tokens, _EndLine}, ""} = erl_scan:tokens("", Str, _L=1), 776: {ok, Exprs} = erl_parse:parse_exprs(Tokens), 777: "S = hopp, {hej,S}" = lists:flatten(erl_pp:exprs(Exprs)), 778: ok. 779: 780: otp_6911(doc) -> 781: "OTP_6911. More newlines."; 782: otp_6911(suite) -> []; 783: otp_6911(Config) when is_list(Config) -> 784: F = {function,5,thomas,1, 785: [{clause,5, 786: [{var,5,'X'}], 787: [], 788: [{'case',6, 789: {var,6,'X'}, 790: [{clause,7,[{atom,7,true}],[],[{integer,7,12}]}, 791: {clause,8,[{atom,8,false}],[],[{integer,8,14}]}]}]}]}, 792: ?line Chars = lists:flatten(erl_pp:form(F)), 793: ?line "thomas(X) ->\n" 794: " case X of\n" 795: " true ->\n" 796: " 12;\n" 797: " false ->\n" 798: " 14\n" 799: " end.\n" = Chars, 800: ?line ok = pp_expr(<<"case X of true -> 12; false -> 14 end">>), 801: ?line ok = pp_expr(<<"receive after 1 -> ok end">>), 802: ok. 803: 804: otp_6914(doc) -> 805: "OTP_6914. Binary comprehensions."; 806: otp_6914(suite) -> []; 807: otp_6914(Config) when is_list(Config) -> 808: ?line ok = pp_expr(<<"<< <<B:1>> || B <- [0,1,1] >>">>), 809: ?line ok = pp_expr(<<"[ B || <<B:1>> <= <<\"hi\">>]">>), 810: ?line ok = pp_expr(<<"<< <<1:1>> || true >>">>), 811: ok. 812: 813: otp_8150(doc) -> 814: "OTP_8150. Types."; 815: otp_8150(suite) -> []; 816: otp_8150(Config) when is_list(Config) -> 817: ?line _ = [{N,ok} = {N,pp_forms(B)} || 818: {N,B} <- type_examples() 819: ], 820: ok. 821: 822: otp_8238(doc) -> 823: "OTP_8238. Bugfix 'E'."; 824: otp_8238(suite) -> []; 825: otp_8238(Config) when is_list(Config) -> 826: Ex = [<<"-record(rec1, {}).\n" 827: "-record(rec2, {a, b}).\n" 828: "-record(rec3, {f123, g, h}).\n">>, 829: <<"-type line() :: integer().\n">>, 830: <<"-type info_line() :: integer() | term().\n">>, 831: <<"-type column() :: pos_integer().\n">>, 832: [["\n", B] || {_,B} <- type_examples()], 833: <<"t1(T) ->\n" 834: " foo:bar(#rec1{}, #rec2{}),\n" 835: " T.\n" 836: "t2() ->\n" 837: " #r{}.\n">> 838: ], 839: ?line compile(Config, [{otp_8238,iolist_to_binary(Ex)}]), 840: ok. 841: 842: type_examples() -> 843: [{ex1,<<"-type ann() :: Var :: integer(). ">>}, 844: {ex2,<<"-type ann2() :: Var :: " 845: "'return' | 'return_white_spaces' | 'return_comments'" 846: " | 'text' | ann(). ">>}, 847: {ex3,<<"-type paren() :: (ann2()). ">>}, 848: {ex4,<<"-type t1() :: atom(). ">>}, 849: {ex5,<<"-type t2() :: [t1()]. ">>}, 850: {ex6,<<"-type t3(Atom) :: integer(Atom). ">>}, 851: {ex7,<<"-type '\\'t::4'() :: t3('\\'foobar'). ">>}, 852: {ex8,<<"-type t5() :: {t1(), t3(foo)}. ">>}, 853: {ex9,<<"-type t6() :: 1 | 2 | 3 | 'foo' | 'bar'. ">>}, 854: {ex10,<<"-type t7() :: []. ">>}, 855: {ex11,<<"-type t71() :: [_]. ">>}, 856: {ex12,<<"-type t8() :: {any(),none(),pid(),port()," 857: "reference(),float()}. ">>}, 858: {ex13,<<"-type t9() :: [1|2|3|foo|bar] | " 859: "list(a | b | c) | t71(). ">>}, 860: {ex14,<<"-type t10() :: {1|2|3|foo|t9()} | {}. ">>}, 861: {ex15,<<"-type t11() :: 1..2. ">>}, 862: {ex16,<<"-type t13() :: maybe_improper_list(integer(), t11()). ">>}, 863: {ex17,<<"-type t14() :: [erl_scan:foo() | " 864: "erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)]. ">>}, 865: {ex18,<<"-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>," 866: "<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|" 867: "<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|" 868: "<<_:34>>|<<_:34>>|<<_:34>>]. ">>}, 869: {ex19,<<"-type t16() :: fun(). ">>}, 870: {ex20,<<"-type t17() :: fun((...) -> paren()). ">>}, 871: {ex21,<<"-type t18() :: fun(() -> t17() | t16()). ">>}, 872: {ex22,<<"-type t19() :: fun((t18()) -> t16()) |" 873: "fun((nonempty_maybe_improper_list('integer', any())|" 874: "1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->" 875: "nonempty_maybe_improper_list('integer', any())|" 876: "1|2|3|a|b|<<_:3,_:_*14>>|integer()). ">>}, 877: {ex23,<<"-type t20() :: [t19(), ...]. ">>}, 878: {ex24,<<"-type t21() :: tuple(). ">>}, 879: {ex25,<<"-type t21(A) :: A. ">>}, 880: {ex26,<<"-type t22() :: t21(integer()). ">>}, 881: {ex27,<<"-type t23() :: #rec1{}. ">>}, 882: {ex28,<<"-type t24() :: #rec2{a :: t23(), b :: [atom()]}. ">>}, 883: {ex29,<<"-type t25() :: #rec3{f123 :: [t24() | " 884: "1|2|3|4|a|b|c|d| " 885: "nonempty_maybe_improper_list(integer, any())]}. ">>}, 886: {ex30,<<"-type t99() ::" 887: "{t2(),'\\'t::4'(),t5(),t6(),t7(),t8(),t10(),t14()," 888: "t15(),t20(),t21(), t22(),t25()}. ">>}, 889: {ex31,<<"-spec t1(FooBar :: t99()) -> t99();" 890: "(t2()) -> t2();" 891: "('\\'t::4'()) -> '\\'t::4'() when is_subtype('\\'t::4'(), t24);" 892: "(t23()) -> t23() when is_subtype(t23(), atom())," 893: " is_subtype(t23(), t14());" 894: "(t24()) -> t24() when is_subtype(t24(), atom())," 895: " is_subtype(t24(), t14())," 896: " is_subtype(t24(), '\\'t::4'()).">>}, 897: {ex32,<<"-spec mod:t2() -> any(). ">>}, 898: {ex33,<<"-opaque attributes_data() :: " 899: "[{'column', column()} | {'line', info_line()} |" 900: " {'text', string()}] | {line(),column()}. ">>}, 901: {ex34,<<"-record(r,{" 902: "f1 :: attributes_data()," 903: "f222 = foo:bar(34, #rec3{}, 234234234423, " 904: " aassdsfsdfsdf, 2234242323) :: " 905: " [t24() | 1|2|3|4|a|b|c|d| " 906: " nonempty_maybe_improper_list(integer, any())]," 907: "f333 :: [t24() | 1|2|3|4|a|b|c|d| " 908: " nonempty_maybe_improper_list(integer, any())]," 909: "f3 = x:y()," 910: "f4 = x:z() :: t99()," 911: "f17 :: 'undefined'," 912: "f18 :: 1 | 2 | 'undefined'," 913: "f19 = 3 :: integer()|undefined," 914: "f5 = 3 :: undefined|integer()}). ">>}]. 915: 916: otp_8473(doc) -> 917: "OTP_8473. Bugfix abstract type 'fun'."; 918: otp_8473(suite) -> []; 919: otp_8473(Config) when is_list(Config) -> 920: Ex = [{ex1,<<"-type 'fun'(A) :: A.\n" 921: "-type funkar() :: 'fun'(fun((integer()) -> atom())).\n">>}], 922: ?line _ = [{N,ok} = {N,pp_forms(B)} || 923: {N,B} <- Ex], 924: ok. 925: 926: otp_8522(doc) -> 927: "OTP_8522. Avoid duplicated 'undefined' in record field types."; 928: otp_8522(suite) -> []; 929: otp_8522(Config) when is_list(Config) -> 930: FileName = filename('otp_8522.erl', Config), 931: C = <<"-module(otp_8522).\n" 932: "-record(r, {f1 :: undefined,\n" 933: " f2 :: A :: undefined,\n" 934: " f3 :: (undefined),\n" 935: " f4 :: x | y | undefined | z,\n" 936: " f5 :: a}).\n">>, 937: ?line ok = file:write_file(FileName, C), 938: ?line {ok, _} = compile:file(FileName, [{outdir,?privdir},debug_info]), 939: BF = filename("otp_8522", Config), 940: ?line {ok, A} = beam_lib:chunks(BF, [abstract_code]), 941: ?line 5 = count_atom(A, undefined), 942: ok. 943: 944: count_atom(A, A) -> 945: 1; 946: count_atom(T, A) when is_tuple(T) -> 947: count_atom(tuple_to_list(T), A); 948: count_atom(L, A) when is_list(L) -> 949: lists:sum([count_atom(T, A) || T <- L]); 950: count_atom(_, _) -> 951: 0. 952: 953: otp_8567(doc) -> 954: "OTP_8567. Avoid duplicated 'undefined' in record field types."; 955: otp_8567(suite) -> []; 956: otp_8567(Config) when is_list(Config) -> 957: FileName = filename('otp_8567.erl', Config), 958: C = <<"-module otp_8567.\n" 959: "-compile export_all.\n" 960: "-spec(a).\n" 961: "-record r, {a}.\n" 962: "-record s, {a :: integer()}.\n" 963: "-type t() :: {#r{},#s{}}.\n">>, 964: ?line ok = file:write_file(FileName, C), 965: ?line {error,[{_,[{3,erl_parse,["syntax error before: ","')'"]}]}],_} = 966: compile:file(FileName, [return]), 967: 968: F = <<"-module(otp_8567).\n" 969: "-compile(export_all).\n" 970: "-record(t, {a}).\n" 971: "-record(u, {a :: integer()}).\n" 972: "-opaque ot() :: {#t{}, #u{}}.\n" 973: "-opaque(ot1() :: atom()).\n" 974: "-type a() :: integer().\n" 975: "-spec t() -> a().\n" 976: "t() ->\n" 977: " 3.\n" 978: "\n" 979: "-spec(t1/1 :: (ot()) -> ot1()).\n" 980: "t1(A) ->\n" 981: " A.\n" 982: "\n" 983: "-spec(t2 (ot()) -> ot1()).\n" 984: "t2(A) ->\n" 985: " A.\n" 986: "\n" 987: "-spec(otp_8567:t3/1 :: (ot()) -> ot1()).\n" 988: "t3(A) ->\n" 989: " A.\n" 990: "\n" 991: "-spec(otp_8567:t4 (ot()) -> ot1()).\n" 992: "t4(A) ->\n" 993: " A.\n">>, 994: ?line ok = pp_forms(F), 995: 996: ok. 997: 998: otp_8664(doc) -> 999: "OTP_8664. Types with integer expressions."; 1000: otp_8664(suite) -> []; 1001: otp_8664(Config) when is_list(Config) -> 1002: FileName = filename('otp_8664.erl', Config), 1003: C1 = <<"-module(otp_8664).\n" 1004: "-export([t/0]).\n" 1005: "-define(A, -3).\n" 1006: "-define(B, (?A*(-1 band (((2)))))).\n" 1007: "-type t1() :: ?B | ?A.\n" 1008: "-type t2() :: ?B-1 .. -?B.\n" 1009: "-type t3() :: 9 band (8 - 3) | 1+2 | 5 band 3.\n" 1010: "-type b1() :: <<_:_*(3-(-1))>>\n" 1011: " | <<_:(-(?B))>>\n" 1012: " | <<_:4>>.\n" 1013: "-type u() :: 1 .. 2 | 3.. 4 | (8-3) ..6 | 5+0..6.\n" 1014: "-type t() :: t1() | t2() | t3() | b1() | u().\n" 1015: "-spec t() -> t().\n" 1016: "t() -> 3.\n">>, 1017: ?line ok = file:write_file(FileName, C1), 1018: ?line {ok, _, []} = compile:file(FileName, [return]), 1019: 1020: C2 = <<"-module(otp_8664).\n" 1021: "-export([t/0]).\n" 1022: "-spec t() -> 9 and 4.\n" 1023: "t() -> 0.\n">>, 1024: ?line ok = file:write_file(FileName, C2), 1025: ?line {error,[{_,[{3,erl_lint,{type_syntax,integer}}]}],_} = 1026: compile:file(FileName, [return]), 1027: 1028: ok. 1029: 1030: otp_9147(doc) -> 1031: "OTP_9147. Create well-formed types when adding 'undefined'."; 1032: otp_9147(suite) -> []; 1033: otp_9147(Config) when is_list(Config) -> 1034: FileName = filename('otp_9147.erl', Config), 1035: C1 = <<"-module(otp_9147).\n" 1036: "-export_type([undef/0]).\n" 1037: "-record(undef, {f1 :: F1 :: a | b}).\n" 1038: "-type undef() :: #undef{}.\n">>, 1039: ?line ok = file:write_file(FileName, C1), 1040: ?line {ok, _, []} = 1041: compile:file(FileName, [return,'P',{outdir,?privdir}]), 1042: PFileName = filename('otp_9147.P', Config), 1043: ?line {ok, Bin} = file:read_file(PFileName), 1044: %% The parentheses around "F1 :: a | b" are new (bugfix). 1045: ?line true = 1046: lists:member("-record(undef,{f1 :: undefined | (F1 :: a | b)}).", 1047: string:tokens(binary_to_list(Bin), "\n")), 1048: ok. 1049: 1050: otp_10302(doc) -> 1051: "OTP-10302. Unicode characters scanner/parser."; 1052: otp_10302(suite) -> []; 1053: otp_10302(Config) when is_list(Config) -> 1054: Ts = [{uni_1, 1055: <<"t() -> <<(<<\"abc\\x{aaa}\">>):3/binary>>.">>} 1056: ], 1057: compile(Config, Ts), 1058: ok = pp_expr(<<"$\\x{aaa}">>), 1059: ok = pp_expr(<<"\"1\\x{aaa}\"">>), 1060: ok = pp_expr(<<"<<<<\"hej\">>/binary>>">>), 1061: ok = pp_expr(<<"<< <<\"1\\x{aaa}\">>/binary>>">>), 1062: 1063: U = [{encoding,unicode}], 1064: 1065: do_hook(fun(H) -> [{hook,H}] end), 1066: do_hook(fun(H) -> [{hook,H}]++U end), 1067: 1068: ok = pp_expr(<<"$\\x{aaa}">>, [{hook,fun hook/4}]), 1069: 1070: Opts = [{hook, fun unicode_hook/4},{encoding,unicode}], 1071: Lc = parse_expr("[X || X <- [\"\x{400}\",\"\xFF\"]]."), 1072: Expr = {call,0,{atom,0,fff},[{foo,{foo,Lc}},{foo,{foo,Lc}}]}, 1073: EChars = lists:flatten(erl_pp:expr(Expr, 0, Opts)), 1074: Call = {call,0,{atom,0,foo},[{call,0,{atom,0,foo},[Lc]}]}, 1075: Expr2 = {call,0,{atom,0,fff},[Call,Call]}, 1076: EChars2 = erl_pp:exprs([Expr2], U), 1077: EChars = lists:flatten(EChars2), 1078: [$\x{400},$\x{400}] = [C || C <- EChars, C > 255], 1079: 1080: ok = pp_forms(<<"function() -> {\"\x{400}\",$\x{400}}. "/utf8>>, U), 1081: ok = pp_forms("function() -> {\"\x{400}\",$\x{400}}. ", []), 1082: ok. 1083: 1084: unicode_hook({foo,E}, I, P, H) -> 1085: erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H). 1086: 1087: otp_10820(doc) -> 1088: "OTP-10820. Unicode filenames."; 1089: otp_10820(suite) -> []; 1090: otp_10820(Config) when is_list(Config) -> 1091: C1 = <<"%% coding: utf-8\n -module(any).">>, 1092: ok = do_otp_10820(Config, C1, "+pc latin1"), 1093: ok = do_otp_10820(Config, C1, "+pc unicode"), 1094: C2 = <<"-module(any).">>, 1095: ok = do_otp_10820(Config, C2, "+pc latin1"), 1096: ok = do_otp_10820(Config, C2, "+pc unicode"). 1097: 1098: do_otp_10820(Config, C, PC) -> 1099: {ok,Node} = start_node(erl_pp_helper, "+fnu " ++ PC), 1100: L = [915,953,959,973,957,953,954,959,957,964], 1101: FileName = filename(L++".erl", Config), 1102: ok = rpc:call(Node, file, write_file, [FileName, C]), 1103: {ok, _, []} = rpc:call(Node, compile, file, 1104: [FileName, [return,'P',{outdir,?privdir}]]), 1105: PFileName = filename(L++".P", Config), 1106: {ok, Bin} = rpc:call(Node, file, read_file, [PFileName]), 1107: true = test_server:stop_node(Node), 1108: true = file_attr_is_string(binary_to_list(Bin)), 1109: ok. 1110: 1111: file_attr_is_string("-file(\"" ++ _) -> true; 1112: file_attr_is_string([_ | L]) -> 1113: file_attr_is_string(L). 1114: 1115: otp_11100(doc) -> 1116: "OTP-11100. Fix printing of invalid forms."; 1117: otp_11100(suite) -> []; 1118: otp_11100(Config) when is_list(Config) -> 1119: %% There are a few places where the added code ("options(none)") 1120: %% doesn't make a difference (pp:bit_elem_type/1 is an example). 1121: 1122: %% Cannot trigger the use of the hook function with export/import. 1123: "-export([{fy,a}/b]).\n" = 1124: pf({attribute,1,export,[{{fy,a},b}]}), 1125: "-type foo() :: integer(INVALID-FORM:{foo,bar}:).\n" = 1126: pf({attribute,1,type,{foo,{type,1,integer,[{foo,bar}]},[]}}), 1127: pf({attribute,1,type, 1128: {a,{type,1,range,[{integer,1,1},{foo,bar}]},[]}}), 1129: "-type foo(INVALID-FORM:{foo,bar}:) :: A.\n" = 1130: pf({attribute,1,type,{foo,{var,1,'A'},[{foo,bar}]}}), 1131: "-type foo() :: (INVALID-FORM:{foo,bar}: :: []).\n" = 1132: pf({attribute,1,type, 1133: {foo,{paren_type,1, 1134: [{ann_type,1,[{foo,bar},{type,1,nil,[]}]}]}, 1135: []}}), 1136: "-type foo() :: <<_:INVALID-FORM:{foo,bar}:>>.\n" = 1137: pf({attribute,1,type, 1138: {foo,{type,1,binary,[{foo,bar},{integer,1,0}]},[]}}), 1139: "-type foo() :: <<_:10, _:_*INVALID-FORM:{foo,bar}:>>.\n" = 1140: pf({attribute,1,type, 1141: {foo,{type,1,binary,[{integer,1,10},{foo,bar}]},[]}}), 1142: "-type foo() :: #r{INVALID-FORM:{foo,bar}: :: integer()}.\n" = 1143: pf({attribute,1,type, 1144: {foo,{type,1,record, 1145: [{atom,1,r}, 1146: {type,1,field_type, 1147: [{foo,bar},{type,1,integer,[]}]}]}, 1148: []}}), 1149: ok. 1150: 1151: pf(Form) -> 1152: lists:flatten(erl_pp:form(Form,none)). 1153: 1154: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1155: 1156: compile(Config, Tests) -> 1157: F = fun({N,P}, BadL) -> 1158: case catch compile_file(Config, P) of 1159: ok -> 1160: case pp_forms(P) of 1161: ok -> 1162: BadL; 1163: not_ok -> 1164: ?t:format("~nTest ~p failed.~n", [N]), 1165: fail() 1166: end; 1167: Bad -> 1168: ?t:format("~nTest ~p failed. got~n ~p~n", 1169: [N, Bad]), 1170: fail() 1171: end 1172: end, 1173: lists:foldl(F, [], Tests). 1174: 1175: compile_file(Config, Test0) -> 1176: case compile_file(Config, Test0, ['E']) of 1177: {ok, RootFile} -> 1178: File = RootFile ++ ".E", 1179: {ok, Bin0} = file:read_file(File), 1180: Bin = strip_module_info(Bin0), 1181: %% A very simple check: just try to compile the output. 1182: case compile_file(Config, Bin, []) of 1183: {ok, RootFile2} -> 1184: File2 = RootFile2 ++ ".E", 1185: {ok, Bin1} = file:read_file(File2), 1186: case Bin0 =:= Bin1 of 1187: true -> 1188: test_max_line(binary_to_list(Bin)); 1189: false -> 1190: {error, file_contents_modified, {Bin0, Bin1}} 1191: end; 1192: Error -> 1193: {error, could_not_compile_E_file, Error} 1194: end; 1195: Error -> 1196: Error 1197: end. 1198: 1199: compile_file(Config, Test0, Opts0) -> 1200: FileName = filename('erl_pp_test.erl', Config), 1201: Test = list_to_binary(["-module(erl_pp_test). " 1202: "-compile(export_all). ", 1203: Test0]), 1204: Opts = [export_all,return,nowarn_unused_record,{outdir,?privdir} | Opts0], 1205: ok = file:write_file(FileName, Test), 1206: case compile:file(FileName, Opts) of 1207: {ok, _M, _Ws} -> 1208: {ok, filename:rootname(FileName)}; 1209: Error -> Error 1210: end. 1211: 1212: strip_module_info(Bin) -> 1213: {match, [{Start,_Len}|_]} = re:run(Bin, "module_info"), 1214: <<R:Start/binary,_/binary>> = Bin, 1215: R. 1216: 1217: flat_expr(Expr) -> 1218: lists:flatten(erl_pp:expr(Expr, -1, none)). 1219: 1220: pp_forms(Bin) -> 1221: pp_forms(Bin, none). 1222: 1223: pp_forms(Bin, Options) when is_binary(Bin) -> 1224: pp_forms(to_list(Bin, Options), Options); 1225: pp_forms(List, Options) when is_list(List) -> 1226: PP1 = (catch parse_and_pp_forms(List, Options)), 1227: PP2 = (catch parse_and_pp_forms(PP1, Options)), 1228: case PP1 =:= PP2 of % same line numbers 1229: true -> 1230: test_max_line(PP1); 1231: false -> 1232: not_ok 1233: end. 1234: 1235: parse_and_pp_forms(String, Options) -> 1236: lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Options) 1237: end, parse_forms(String))). 1238: 1239: parse_forms(Chars) -> 1240: String = lists:flatten(Chars), 1241: parse_forms2(String, [], 1, []). 1242: 1243: parse_forms2([], _Cont, _Line, Forms) -> 1244: lists:reverse(Forms); 1245: parse_forms2(String, Cont0, Line, Forms) -> 1246: case erl_scan:tokens(Cont0, String, Line, [unicode]) of 1247: {done, {ok, Tokens, EndLine}, Chars} -> 1248: {ok, Form} = erl_parse:parse_form(Tokens), 1249: parse_forms2(Chars, [], EndLine, [Form | Forms]); 1250: {more, Cont} when element(4, Cont) =:= [] -> 1251: %% extra spaces after forms... 1252: parse_forms2([], Cont, Line, Forms); 1253: {more, Cont} -> 1254: %% final dot needs a space... 1255: parse_forms2(" ", Cont, Line, Forms) 1256: end. 1257: 1258: pp_expr(Bin) -> 1259: pp_expr(Bin, none). 1260: 1261: %% Final dot is added. 1262: pp_expr(Bin, Options) when is_binary(Bin) -> 1263: pp_expr(to_list(Bin, Options), Options); 1264: pp_expr(List, Options) when is_list(List) -> 1265: PP1 = (catch parse_and_pp_expr(List, 0, Options)), 1266: PPneg = (catch parse_and_pp_expr(List, -1, Options)), 1267: PP2 = (catch parse_and_pp_expr(PPneg, 0, Options)), 1268: if 1269: PP1 =:= PP2 -> % same line numbers 1270: case 1271: (test_max_line(PP1) =:= ok) and (test_new_line(PPneg) =:= ok) 1272: of 1273: true -> 1274: ok; 1275: false -> 1276: not_ok 1277: end; 1278: true -> 1279: not_ok 1280: end. 1281: 1282: parse_and_pp_expr(String, Indent, Options) -> 1283: StringDot = lists:flatten(String) ++ ".", 1284: erl_pp:expr(parse_expr(StringDot), Indent, Options). 1285: 1286: parse_expr(Chars) -> 1287: {ok, Tokens, _} = erl_scan:string(Chars, 1, [unicode]), 1288: {ok, [Expr]} = erl_parse:parse_exprs(Tokens), 1289: Expr. 1290: 1291: to_list(Bin, Options) when is_list(Options) -> 1292: case proplists:get_value(encoding, Options) of 1293: unicode -> unicode:characters_to_list(Bin); 1294: encoding -> binary_to_list(Bin); 1295: undefined -> binary_to_list(Bin) 1296: end; 1297: to_list(Bin, _Hook) -> 1298: binary_to_list(Bin). 1299: 1300: test_new_line(String) -> 1301: case string:chr(String, $\n) of 1302: 0 -> ok; 1303: _ -> not_ok 1304: end. 1305: 1306: test_max_line(String) -> 1307: case max_line(String) of 1308: ML when ML > 100 -> 1309: {error, max_line_too_big, {ML,String}}; 1310: _ML -> 1311: ok 1312: end. 1313: 1314: max_line(String) -> 1315: lists:max([0 | [length(Sub) || 1316: Sub <- string:tokens(String, "\n"), 1317: string:substr(Sub, 1, 5) =/= "-file"]]). 1318: 1319: filename(Name, Config) when is_atom(Name) -> 1320: filename(atom_to_list(Name), Config); 1321: filename(Name, Config) -> 1322: filename:join(?privdir, Name). 1323: 1324: fail() -> 1325: io:format("failed~n"), 1326: ?t:fail(). 1327: 1328: %% +fnu means a peer node has to be started; slave will not do 1329: start_node(Name, Xargs) -> 1330: ?line PA = filename:dirname(code:which(?MODULE)), 1331: test_server:start_node(Name, peer, [{args, "-pa " ++ PA ++ " " ++ Xargs}]).