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