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}]).