1%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- 2%% -------------------------------------------------- 3%% This file is provided to you under the Apache License, 4%% Version 2.0 (the "License"); you may not use this file 5%% except in compliance with the License. You may obtain 6%% a copy of the License at 7%% 8%% http://www.apache.org/licenses/LICENSE-2.0 9%% 10%% Unless required by applicable law or agreed to in writing, 11%% software distributed under the License is distributed on an 12%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13%% KIND, either express or implied. See the License for the 14%% specific language governing permissions and limitations 15%% under the License. 16%% -------------------------------------------------- 17%% File : parse_trans.erl 18%% @author : Ulf Wiger <ulf@wiger.net> 19%% @end 20%% Description : 21%% 22%% Created : 13 Feb 2006 by Ulf Wiger <ulf@wiger.net> (then Ericsson) 23%%%------------------------------------------------------------------- 24 25%% @doc Generic parse transform library for Erlang. 26%% 27%% <p>...</p> 28%% 29%% @end 30 31-module(parse_trans). 32 33-export([plain_transform/2]). 34 35-export([ 36 inspect/4, 37 transform/4, 38 depth_first/4, 39 revert/1, 40 revert_form/1, 41 format_exception/2, format_exception/3, 42 return/2 43 ]). 44 45-export([ 46 error/3, 47 format_error/1 48 ]). 49 50-export([ 51 initial_context/2, 52 do_inspect/4, 53 do_transform/4, 54 do_depth_first/4, 55 top/3 56 ]). 57 58-export([do_insert_forms/4, 59 replace_function/4, 60 replace_function/5, 61 export_function/3]). 62 63-export([ 64 context/2, 65 get_pos/1, 66 get_file/1, 67 get_module/1, 68 get_attribute/2, 69 get_attribute/3, 70 get_orig_syntax_tree/1, 71 function_exists/3, 72 optionally_pretty_print/3, 73 pp_src/2, 74 pp_beam/1, pp_beam/2 75 ]). 76 77-import(erl_syntax, [atom_value/1, 78 attribute_name/1, 79 attribute_arguments/1, 80 string_value/1, 81 type/1 82 ]). 83 84-record(context, {module, 85 function, 86 arity, 87 file, 88 options}). 89 90%% Useful macros for debugging and error reporting 91-define(HERE, {?MODULE, ?LINE}). 92 93-define(DUMMY_LINE, 9999). 94 95-define(ERROR(R, F, I, Trace), 96 begin 97 rpt_error(R, F, I, Trace), 98 throw({error,get_pos(I),{R, Trace}}) 99 end). 100 101-ifdef(OTP_RELEASE). 102-define(WITH_STACKTRACE(T, R, S), T:R:S ->). 103-else. 104-define(WITH_STACKTRACE(T, R, S), T:R -> S = erlang:get_stacktrace(),). 105-endif. 106 107-export_type([forms/0]). 108 109%% Typedefs 110-type form() :: any(). 111-type forms() :: [form()]. 112-type options() :: [{atom(), any()}]. 113-type type() :: atom(). 114-type xform_f_rec() :: fun((type(), form(), #context{}, Acc) -> 115 {form(), boolean(), Acc} 116 | {forms(), form(), forms(), boolean(), Acc}). 117-type xform_f_df() :: fun((type(), form(), #context{}, Acc) -> 118 {form(), Acc} 119 | {forms(), form(), forms(), Acc}). 120-type insp_f() :: fun((type(), form(), #context{}, A) -> {boolean(), A}). 121 122 123%%% @spec (Reason, Form, Info) -> throw() 124%%% Info = [{Key,Value}] 125%%% 126%%% @doc 127%%% <p>Used to report errors detected during the parse transform.</p> 128%%% @end 129%%% 130-spec error(string(), any(), [{any(),any()}]) -> 131 none(). 132error(R, _F, I) -> 133 ST = erlang:process_info(self(), current_stacktrace), 134 % rpt_error(R, F, I, ST), 135 throw({error,get_pos(I),{R, ST}}). 136 137%% @spec plain_transform(Fun, Forms) -> forms() 138%% Fun = function() 139%% Forms = forms() 140%% 141%% @doc 142%% Performs a transform of `Forms' using the fun `Fun(Form)'. `Form' is always 143%% an Erlang abstract form, i.e. it is not converted to syntax_tools 144%% representation. The intention of this transform is for the fun to have a 145%% catch-all clause returning `continue'. This will ensure that it stays robust 146%% against additions to the language. 147%% 148%% `Fun(Form)' must return either of the following: 149%% 150%% * `NewForm' - any valid form 151%% * `continue' - dig into the sub-expressions of the form 152%% * `{done, NewForm}' - Replace `Form' with `NewForm'; return all following 153%% forms unchanged 154%% * `{error, Reason}' - Abort transformation with an error message. 155%% 156%% Example - This transform fun would convert all instances of `P ! Msg' to 157%% `gproc:send(P, Msg)': 158%% <pre> 159%% parse_transform(Forms, _Options) -> 160%% parse_trans:plain_transform(fun do_transform/1, Forms). 161%% 162%% do_transform({'op', L, '!', Lhs, Rhs}) -> 163%% [NewLhs] = parse_trans:plain_transform(fun do_transform/1, [Lhs]), 164%% [NewRhs] = parse_trans:plain_transform(fun do_transform/1, [Rhs]), 165%% {call, L, {remote, L, {atom, L, gproc}, {atom, L, send}}, 166%% [NewLhs, NewRhs]}; 167%% do_transform(_) -> 168%% continue. 169%% </pre> 170%% @end 171%% 172plain_transform(Fun, Forms) when is_function(Fun, 1), is_list(Forms) -> 173 plain_transform1(Fun, Forms). 174 175plain_transform1(_, []) -> 176 []; 177plain_transform1(Fun, [F|Fs]) when is_atom(element(1,F)) -> 178 case Fun(F) of 179 skip -> 180 plain_transform1(Fun, Fs); 181 continue -> 182 [list_to_tuple(plain_transform1(Fun, tuple_to_list(F))) | 183 plain_transform1(Fun, Fs)]; 184 {done, NewF} -> 185 [NewF | Fs]; 186 {error, Reason} -> 187 error(Reason, F, [{form, F}]); 188 NewF when is_tuple(NewF) -> 189 [NewF | plain_transform1(Fun, Fs)] 190 end; 191plain_transform1(Fun, [L|Fs]) when is_list(L) -> 192 [plain_transform1(Fun, L) | plain_transform1(Fun, Fs)]; 193plain_transform1(Fun, [F|Fs]) -> 194 [F | plain_transform1(Fun, Fs)]; 195plain_transform1(_, F) -> 196 F. 197 198 199%% @spec (list()) -> integer() 200%% 201%% @doc 202%% Tries to retrieve the line number from an erl_syntax form. Returns a 203%% (very high) dummy number if not successful. 204%% @end 205%% 206-spec get_pos(list()) -> 207 integer(). 208get_pos(I) when is_list(I) -> 209 case proplists:get_value(form, I) of 210 undefined -> 211 ?DUMMY_LINE; 212 Form -> 213 erl_syntax:get_pos(Form) 214 end. 215 216 217%%% @spec (Forms) -> string() 218%%% @doc 219%%% Returns the name of the file being compiled. 220%%% @end 221%%% 222-spec get_file(forms()) -> 223 string(). 224get_file(Forms) -> 225 string_value(hd(get_attribute(file, Forms, [erl_syntax:string("undefined")]))). 226 227 228 229%%% @spec (Forms) -> atom() 230%%% @doc 231%%% Returns the name of the module being compiled. 232%%% @end 233%%% 234-spec get_module([any()]) -> 235 atom(). 236get_module(Forms) -> 237 atom_value(hd(get_attribute(module, Forms, [erl_syntax:atom(undefined)]))). 238 239 240 241%%% @spec (A, Forms) -> any() 242%%% A = atom() 243%%% 244%%% @doc 245%%% Returns the value of the first occurence of attribute A. 246%%% @end 247%%% 248-spec get_attribute(atom(), [any()]) -> 249 'none' | [erl_syntax:syntaxTree()]. 250%% 251get_attribute(A, Forms) -> get_attribute(A,Forms,[erl_syntax:atom(undefined)]). 252get_attribute(A, Forms, Undef) -> 253 case find_attribute(A, Forms) of 254 false -> 255 Undef; 256 Other -> 257 Other 258 end. 259 260find_attribute(A, [F|Forms]) -> 261 case type(F) == attribute 262 andalso atom_value(attribute_name(F)) == A of 263 true -> 264 attribute_arguments(F); 265 false -> 266 find_attribute(A, Forms) 267 end; 268find_attribute(_, []) -> 269 false. 270 271%% @spec (Fname::atom(), Arity::integer(), Forms) -> boolean() 272%% 273%% @doc 274%% Checks whether the given function is defined in Forms. 275%% @end 276%% 277-spec function_exists(atom(), integer(), forms()) -> 278 boolean(). 279function_exists(Fname, Arity, Forms) -> 280 Fns = proplists:get_value( 281 functions, erl_syntax_lib:analyze_forms(Forms), []), 282 lists:member({Fname,Arity}, Fns). 283 284 285%%% @spec (Forms, Options) -> #context{} 286%%% 287%%% @doc 288%%% Initializes a context record. When traversing through the form 289%%% list, the context is updated to reflect the current function and 290%%% arity. Static elements in the context are the file name, the module 291%%% name and the options passed to the transform function. 292%%% @end 293%%% 294-spec initial_context(forms(), options()) -> 295 #context{}. 296initial_context(Forms, Options) -> 297 File = get_file(Forms), 298 Module = get_module(Forms), 299 #context{file = File, 300 module = Module, 301 options = Options}. 302 303%%% @spec (Fun, Acc, Forms, Options) -> {TransformedForms, NewAcc} 304%%% Fun = function() 305%%% Options = [{Key,Value}] 306%%% 307%%% @doc 308%%% Makes one pass 309%%% @end 310-spec transform(xform_f_rec(), Acc, forms(), options()) -> 311 {forms(), Acc} | {error, list()}. 312transform(Fun, Acc, Forms, Options) when is_function(Fun, 4) -> 313 do(fun do_transform/4, Fun, Acc, Forms, Options). 314 315-spec depth_first(xform_f_df(), Acc, forms(), options()) -> 316 {forms(), Acc} | {error, list()}. 317depth_first(Fun, Acc, Forms, Options) when is_function(Fun, 4) -> 318 do(fun do_depth_first/4, Fun, Acc, Forms, Options). 319 320do(Transform, Fun, Acc, Forms, Options) -> 321 Context = initial_context(Forms, Options), 322 File = Context#context.file, 323 try Transform(Fun, Acc, Forms, Context) of 324 {NewForms, Acc1} when is_list(NewForms) -> 325 NewForms1 = optionally_renumber(NewForms, Options), 326 optionally_pretty_print(NewForms1, Options, Context), 327 {NewForms1, Acc1} 328 catch 329 ?WITH_STACKTRACE(error, Reason, ST) 330 {error, 331 [{File, [{?DUMMY_LINE, ?MODULE, 332 {Reason, ST}}]}]}; 333 throw:{error, Ln, What} -> 334 {error, [{error, {Ln, ?MODULE, What}}]} 335 end. 336 337-spec top(function(), forms(), list()) -> 338 forms() | {error, term()}. 339top(F, Forms, Options) -> 340 Context = initial_context(Forms, Options), 341 File = Context#context.file, 342 try F(Forms, Context) of 343 {error, Reason} -> {error, Reason}; 344 NewForms when is_list(NewForms) -> 345 NewForms1 = optionally_renumber(NewForms, Options), 346 optionally_pretty_print(NewForms1, Options, Context), 347 NewForms1 348 catch 349 ?WITH_STACKTRACE(error, Reason, ST) 350 {error, 351 [{File, [{?DUMMY_LINE, ?MODULE, 352 {Reason, ST}}]}]}; 353 throw:{error, Ln, What} -> 354 {error, [{File, [{Ln, ?MODULE, What}]}], []} 355 end. 356 357replace_function(F, Arity, NewForm, Forms) -> 358 replace_function(F, Arity, NewForm, Forms, []). 359 360replace_function(F, Arity, NewForm, Forms, Opts) -> 361 {NewForms, _} = 362 do_transform( 363 fun(function, Form, _Ctxt, Acc) -> 364 case erl_syntax:revert(Form) of 365 {function, _, F, Arity, _} = RevForm -> 366 {[], NewForm, with_original_f(RevForm, Opts), 367 false, Acc}; 368 _ -> 369 {Form, false, Acc} 370 end; 371 (_, Form, _Ctxt, Acc) -> 372 {Form, false, Acc} 373 end, false, Forms, initial_context(Forms, [])), 374 revert(maybe_export_renamed(NewForms, Arity, Opts)). 375 376with_original_f({function,_,_,_,_} = Form, Opts) -> 377 case lists:keyfind(rename_original, 1, Opts) of 378 {_, NewName} when is_atom(NewName) -> 379 [setelement(3, Form, NewName)]; 380 _ -> 381 [] 382 end. 383 384maybe_export_renamed(Forms, Arity, Opts) -> 385 case lists:keyfind(rename_original, 1, Opts) of 386 {_, NewName} when is_atom(NewName) -> 387 export_function(NewName, Arity, Forms); 388 _ -> 389 Forms 390 end. 391 392export_function(F, Arity, Forms) -> 393 do_insert_forms(above, [{attribute, 1, export, [{F, Arity}]}], Forms, 394 initial_context(Forms, [])). 395 396-spec do_insert_forms(above | below, forms(), forms(), #context{}) -> 397 forms(). 398do_insert_forms(above, Insert, Forms, Context) when is_list(Insert) -> 399 {NewForms, _} = 400 do_transform( 401 fun(function, F, _Ctxt, false) -> 402 {Insert, F, [], _Recurse = false, true}; 403 (_, F, _Ctxt, Acc) -> 404 {F, _Recurse = false, Acc} 405 end, false, Forms, Context), 406 NewForms; 407do_insert_forms(below, Insert, Forms, _Context) when is_list(Insert) -> 408 insert_below(Forms, Insert). 409 410 411insert_below([F|Rest], Insert) -> 412 case type(F) of 413 eof_marker -> 414 Insert ++ [F]; 415 _ -> 416 [F|insert_below(Rest, Insert)] 417 end. 418 419-spec optionally_pretty_print(forms(), options(), #context{}) -> 420 ok. 421optionally_pretty_print(Result, Options, Context) -> 422 DoPP = option_value(pt_pp_src, Options, Result), 423 DoLFs = option_value(pt_log_forms, Options, Result), 424 File = Context#context.file, 425 if DoLFs -> 426 Out1 = outfile(File, forms), 427 {ok,Fd} = file:open(Out1, [write]), 428 try lists:foreach(fun(F) -> io:fwrite(Fd, "~p.~n", [F]) end, Result) 429 after 430 ok = file:close(Fd) 431 end; 432 true -> ok 433 end, 434 if DoPP -> 435 Out2 = outfile(File, pp), 436 pp_src(Result, Out2), 437 io:fwrite("Pretty-printed in ~p~n", [Out2]); 438 true -> ok 439 end. 440 441optionally_renumber(Result, Options) -> 442 case option_value(pt_renumber, Options, Result) of 443 true -> 444 io:fwrite("renumbering...~n", []), 445 Rev = revert(Result), 446 renumber_(Rev); 447 false -> 448 Result 449 end. 450 451renumber_(L) when is_list(L) -> 452 {Result, _} = renumber_(L, 1), 453 Result. 454 455renumber_(L, Acc) when is_list(L) -> 456 lists:mapfoldl(fun renumber_/2, Acc, L); 457renumber_(T, Prev) when is_tuple(T) -> 458 case is_form(T) of 459 true -> 460 New = Prev+1, 461 T1 = setelement(2, T, New), 462 {Res, NewAcc} = renumber_(tuple_to_list(T1), New), 463 {list_to_tuple(Res), NewAcc}; 464 false -> 465 L = tuple_to_list(T), 466 {Res, NewAcc} = renumber_(L, Prev), 467 {list_to_tuple(Res), NewAcc} 468 end; 469renumber_(X, Prev) -> 470 {X, Prev}. 471 472is_form(T) when element(1,T)==type -> true; 473is_form(T) -> 474 try erl_syntax:type(T), 475 true 476 catch 477 error:_ -> 478 false 479 end. 480 481option_value(Key, Options, Result) -> 482 case proplists:get_value(Key, Options) of 483 undefined -> 484 case find_attribute(Key,Result) of 485 [Expr] -> 486 type(Expr) == atom andalso 487 atom_value(Expr) == true; 488 _ -> 489 false 490 end; 491 V when is_boolean(V) -> 492 V 493 end. 494 495 496%%% @spec (Fun, Forms, Acc, Options) -> NewAcc 497%%% Fun = function() 498%%% @doc 499%%% Equvalent to do_inspect(Fun,Acc,Forms,initial_context(Forms,Options)). 500%%% @end 501%%% 502-spec inspect(insp_f(), A, forms(), options()) -> 503 A. 504inspect(F, Acc, Forms, Options) -> 505 Context = initial_context(Forms, Options), 506 do_inspect(F, Acc, Forms, Context). 507 508 509 510outfile(File, Type) -> 511 "lre." ++ RevF = lists:reverse(File), 512 lists:reverse(RevF) ++ ext(Type). 513 514ext(pp) -> ".xfm"; 515ext(forms) -> ".xforms". 516 517%% @spec (Forms, Out::filename()) -> ok 518%% 519%% @doc Pretty-prints the erlang source code corresponding to Forms into Out 520%% 521-spec pp_src(forms(), string()) -> 522 ok. 523pp_src(Res, F) -> 524 parse_trans_pp:pp_src(Res, F). 525%% Str = [io_lib:fwrite("~s~n", 526%% [lists:flatten([erl_pp:form(Fm) || 527%% Fm <- revert(Res)])])], 528%% file:write_file(F, list_to_binary(Str)). 529 530%% @spec (Beam::file:filename()) -> string() | {error, Reason} 531%% 532%% @doc 533%% Reads debug_info from the beam file Beam and returns a string containing 534%% the pretty-printed corresponding erlang source code. 535%% @end 536-spec pp_beam(file:filename()) -> ok. 537pp_beam(Beam) -> 538 parse_trans_pp:pp_beam(Beam). 539 540%% @spec (Beam::filename(), Out::filename()) -> ok | {error, Reason} 541%% 542%% @doc 543%% Reads debug_info from the beam file Beam and pretty-prints it as 544%% Erlang source code, storing it in the file Out. 545%% @end 546%% 547-spec pp_beam(file:filename(), file:filename()) -> ok. 548pp_beam(F, Out) -> 549 parse_trans_pp:pp_beam(F, Out). 550 551 552%%% @spec (File) -> Forms 553%%% 554%%% @doc 555%%% <p>Fetches a Syntax Tree representing the code before pre-processing, 556%%% that is, including record and macro definitions. Note that macro 557%%% definitions must be syntactically complete forms (this function 558%%% uses epp_dodger).</p> 559%%% @end 560%%% 561-spec get_orig_syntax_tree(string()) -> 562 forms(). 563get_orig_syntax_tree(File) -> 564 case epp_dodger:parse_file(File) of 565 {ok, Forms} -> 566 Forms; 567 Err -> 568 error(error_reading_file, ?HERE, [{File,Err}]) 569 end. 570 571%%% @spec (Tree) -> Forms 572%%% 573%%% @doc Reverts back from Syntax Tools format to Erlang forms. 574%%% <p>Note that the Erlang forms are a subset of the Syntax Tools 575%%% syntax tree, so this function is safe to call even on a list of 576%%% regular Erlang forms.</p> 577%%% <p>Note2: R16B03 introduced a bug, where forms produced by 578%%% `erl_syntax:revert/1' (specifically, implicit funs) could crash the linter. 579%%% This function works around that limitation, after first verifying that it's 580%%% necessary to do so. Use of the workaround can be forced with the help of 581%%% the `parse_trans' environment variable {revert_workaround, true}. This 582%%% variable will be removed when R16B03 is no longer 'supported'.</p> 583%%% @end 584%%% 585-spec revert(forms()) -> 586 forms(). 587revert(Tree) when is_list(Tree) -> 588 WorkAround = needs_revert_workaround(), 589 [revert_form(T, WorkAround) || T <- lists:flatten(Tree)]. 590 591%%% @spec (Tree) -> Form 592%%% 593%%% @doc Reverts a single form back from Syntax Tools format to Erlang forms. 594%%% <p>`erl_syntax:revert/1' has had a long-standing bug where it doesn't 595%%% completely revert attribute forms. This function deals properly with those 596%%% cases.</p> 597%%% <p>Note that the Erlang forms are a subset of the Syntax Tools 598%%% syntax tree, so this function is safe to call even on a regular Erlang 599%%% form.</p> 600%%% <p>Note2: R16B03 introduced a bug, where forms produced by 601%%% `erl_syntax:revert/1' (specifically, implicit funs) could crash the linter. 602%%% This function works around that limitation, after first verifying that it's 603%%% necessary to do so. Use of the workaround can be forced with the help of 604%%% the `parse_trans' environment variable {revert_workaround, true}. This 605%%% variable will be removed when R16B03 is no longer 'supported'.</p> 606%%% @end 607revert_form(F) -> 608 revert_form(F, needs_revert_workaround()). 609 610revert_form(F, W) -> 611 case erl_syntax:revert(F) of 612 {attribute,L,A,Tree} when element(1,Tree) == tree -> 613 {attribute,L,A,erl_syntax:revert(Tree)}; 614 Result -> 615 if W -> fix_impl_fun(Result); 616 true -> Result 617 end 618 end. 619 620fix_impl_fun({'fun',L,{function,{atom,_,Fn},{integer,_,Ay}}}) -> 621 {'fun',L,{function,Fn,Ay}}; 622fix_impl_fun({'fun',L,{function,{atom,_,M},{atom,_,Fn},{integer,_,Ay}}}) -> 623 {'fun',L,{function,M,Fn,Ay}}; 624fix_impl_fun(T) when is_tuple(T) -> 625 list_to_tuple([fix_impl_fun(F) || F <- tuple_to_list(T)]); 626fix_impl_fun([H|T]) -> 627 [fix_impl_fun(H) | fix_impl_fun(T)]; 628fix_impl_fun(X) -> 629 X. 630 631needs_revert_workaround() -> 632 case application:get_env(parse_trans,revert_workaround) of 633 {ok, Bool} when is_boolean(Bool) -> Bool; 634 _ -> 635 Res = try lint_reverted() 636 catch 637 error:_ -> 638 true 639 end, 640 application:set_env(parse_trans,revert_workaround,Res), 641 Res 642 end. 643 644lint_reverted() -> 645 Ts = [{attribute,1,module,m}, 646 {attribute,2,export,[{f,0}]}, 647 erl_syntax:function(erl_syntax:atom(f), 648 [erl_syntax:clause( 649 [], 650 [erl_syntax:implicit_fun( 651 erl_syntax:atom(f), 652 erl_syntax:integer(0))])])], 653 Rev = erl_syntax:revert_forms(Ts), 654 erl_lint:module(Rev), 655 false. 656 657 658%%% @spec (Forms, Context) -> Forms | {error,Es,Ws} | {warnings,Forms,Ws} 659%%% 660%%% @doc Checks the transformed result for errors and warnings 661%%% <p>Errors and warnings can be produced from inside a parse transform, with 662%%% a bit of care. The easiest way is to simply produce an `{error, Err}' or 663%%% `{warning, Warn}' form in place. This function finds such forms, and 664%%% removes them from the form list (otherwise, the linter will crash), and 665%%% produces a return value that the compiler can work with.</p> 666%%% 667%%% The format of the `error' and `warning' "forms" must be 668%%% `{Tag, {Pos, Module, Info}}', where: 669%%% <ul> 670%%% <li>`Tag :: error | warning'</li> 671%%% <li>`Pos :: LineNumber | {LineNumber, ColumnNumber}'</li> 672%%% <li>`Module' is a module that exports a corresponding 673%%% `Module:format_error(Info)'</li> 674%%% <li>`Info :: term()'</li> 675%%% </ul> 676%%% <p>If the error is in the form of a caught exception, `Info' may be produced 677%%% using the function {@link format_exception/2}.</p> 678%%% @end 679return(Forms, Context) -> 680 JustForms = plain_transform( 681 fun({error,_}) -> skip; 682 ({warning,_}) -> skip; 683 (_) -> continue 684 end, Forms), 685 File = case Context of 686 #context{file = F} -> F; 687 _ -> "parse_transform" 688 end, 689 case {find_forms(Forms, error), find_forms(Forms, warning)} of 690 {[], []} -> 691 JustForms; 692 {[], Ws} -> 693 {warnings, JustForms, [{File, [W || {warning,W} <- Ws]}]}; 694 {Es, Ws} -> 695 {error, 696 [{File, [E || {error,E} <- Es]}], 697 [{File, [W || {warning,W} <- Ws]}]} 698 end. 699 700find_forms([H|T], Tag) when element(1, H) == Tag -> 701 [H|find_forms(T, Tag)]; 702find_forms([H|T], Tag) when is_tuple(H) -> 703 find_forms(tuple_to_list(H), Tag) ++ find_forms(T, Tag); 704find_forms([H|T], Tag) when is_list(H) -> 705 find_forms(H, Tag) ++ find_forms(T, Tag); 706find_forms([_|T], Tag) -> 707 find_forms(T, Tag); 708find_forms([], _) -> 709 []. 710 711 712-define(LINEMAX, 5). 713-define(CHAR_MAX, 60). 714 715%%% @spec (Class, Reason) -> String 716%%% @equiv format_exception(Class, Reason, 4) 717format_exception(Class, Reason) -> 718 format_exception(Class, Reason, 4). 719 720%%% @spec (Class, Reason, Lines) -> String 721%%% Class = error | throw | exit 722%%% Reason = term() 723%%% Lines = integer() | infinity 724%%% 725%%% @doc Produces a few lines of user-friendly formatting of exception info 726%%% 727%%% This function is very similar to the exception pretty-printing in the shell, 728%%% but returns a string that can be used as error info e.g. by error forms 729%%% handled by {@link return/2}. By default, the first 4 lines of the 730%%% pretty-printed exception info are returned, but this can be controlled 731%%% with the `Lines' parameter. 732%%% 733%%% Note that a stacktrace is generated inside this function. 734%%% @end 735format_exception(Class, Reason, Lines) -> 736 ST = erlang:process_info(self(), current_stacktrace), 737 PrintF = fun(Term, I) -> 738 io_lib_pretty:print( 739 Term, I, columns(), ?LINEMAX, ?CHAR_MAX, 740 record_print_fun()) 741 end, 742 StackF = fun(_, _, _) -> false end, 743 lines(Lines, lib:format_exception( 744 1, Class, Reason, ST, StackF, PrintF)). 745 746columns() -> 747 case io:columns() of 748 {ok, N} -> N; 749 _-> 80 750 end. 751 752lines(infinity, S) -> S; 753lines(N, S) -> 754 [L1|Ls] = re:split(iolist_to_binary([S]), <<"\n">>, [{return,list}]), 755 [L1|["\n" ++ L || L <- lists:sublist(Ls, 1, N-1)]]. 756 757record_print_fun() -> 758 fun(_,_) -> no end. 759 760%%% @spec (Attr, Context) -> any() 761%%% Attr = module | function | arity | options 762%%% 763%%% @doc 764%%% Accessor function for the Context record. 765%%% @end 766-spec context(atom(), #context{}) -> 767 term(). 768context(module, #context{module = M} ) -> M; 769context(function, #context{function = F}) -> F; 770context(arity, #context{arity = A} ) -> A; 771context(file, #context{file = F} ) -> F; 772context(options, #context{options = O} ) -> O. 773 774 775-spec do_inspect(insp_f(), term(), forms(), #context{}) -> 776 term(). 777do_inspect(F, Acc, Forms, Context) -> 778 F1 = 779 fun(Form, Acc0) -> 780 Type = type(Form), 781 {Recurse, Acc1} = apply_F(F, Type, Form, Context, Acc0), 782 if_recurse( 783 Recurse, Form, _Else = Acc1, 784 fun(ListOfLists) -> 785 lists:foldl( 786 fun(L, AccX) -> 787 do_inspect( 788 F, AccX, L, 789 update_context(Form, Context)) 790 end, Acc1, ListOfLists) 791 end) 792 end, 793 lists:foldl(F1, Acc, Forms). 794 795if_recurse(true, Form, Else, F) -> recurse(Form, Else, F); 796if_recurse(false, _, Else, _) -> Else. 797 798recurse(Form, Else, F) -> 799 case erl_syntax:subtrees(Form) of 800 [] -> 801 Else; 802 [_|_] = ListOfLists -> 803 F(ListOfLists) 804 end. 805 806-spec do_transform(xform_f_rec(), term(), forms(), #context{}) -> 807 {forms(), term()}. 808do_transform(F, Acc, Forms, Context) -> 809 Rec = fun do_transform/4, % this function 810 F1 = 811 fun(Form, Acc0) -> 812 {Before1, Form1, After1, Recurse, Acc1} = 813 this_form_rec(F, Form, Context, Acc0), 814 if Recurse -> 815 {NewForm, NewAcc} = 816 enter_subtrees(Form1, F, 817 update_context(Form1, Context), Acc1, Rec), 818 {Before1, NewForm, After1, NewAcc}; 819 true -> 820 {Before1, Form1, After1, Acc1} 821 end 822 end, 823 mapfoldl(F1, Acc, Forms). 824 825-spec do_depth_first(xform_f_df(), term(), forms(), #context{}) -> 826 {forms(), term()}. 827do_depth_first(F, Acc, Forms, Context) -> 828 Rec = fun do_depth_first/4, % this function 829 F1 = 830 fun(Form, Acc0) -> 831 {NewForm, NewAcc} = 832 enter_subtrees(Form, F, Context, Acc0, Rec), 833 this_form_df(F, NewForm, Context, NewAcc) 834 end, 835 mapfoldl(F1, Acc, Forms). 836 837enter_subtrees(Form, F, Context, Acc, Recurse) -> 838 case erl_syntax:subtrees(Form) of 839 [] -> 840 {Form, Acc}; 841 [_|_] = ListOfLists -> 842 {NewListOfLists, NewAcc} = 843 mapfoldl( 844 fun(L, AccX) -> 845 Recurse(F, AccX, L, Context) 846 end, Acc, ListOfLists), 847 NewForm = 848 erl_syntax:update_tree( 849 Form, NewListOfLists), 850 {NewForm, NewAcc} 851 end. 852 853 854this_form_rec(F, Form, Context, Acc) -> 855 Type = type(Form), 856 case apply_F(F, Type, Form, Context, Acc) of 857 {Form1x, Rec1x, A1x} -> 858 {[], Form1x, [], Rec1x, A1x}; 859 {_Be1, _F1, _Af1, _Rec1, _Ac1} = Res1 -> 860 Res1 861 end. 862this_form_df(F, Form, Context, Acc) -> 863 Type = type(Form), 864 case apply_F(F, Type, Form, Context, Acc) of 865 {Form1x, A1x} -> 866 {[], Form1x, [], A1x}; 867 {_Be1, _F1, _Af1, _Ac1} = Res1 -> 868 Res1 869 end. 870 871apply_F(F, Type, Form, Context, Acc) -> 872 try F(Type, Form, Context, Acc) 873 catch 874 ?WITH_STACKTRACE(error, Reason, ST) 875 ?ERROR(Reason, 876 ?HERE, 877 [{type, Type}, 878 {context, Context}, 879 {acc, Acc}, 880 {apply_f, F}, 881 {form, Form}] ++ [{stack, ST}], 882 ST) 883 end. 884 885 886update_context(Form, Context0) -> 887 case type(Form) of 888 function -> 889 {Fun, Arity} = 890 erl_syntax_lib:analyze_function(Form), 891 Context0#context{function = Fun, 892 arity = Arity}; 893 _ -> 894 Context0 895 end. 896 897 898 899 900%%% Slightly modified version of lists:mapfoldl/3 901%%% Here, F/2 is able to insert forms before and after the form 902%%% in question. The inserted forms are not transformed afterwards. 903mapfoldl(F, Accu0, [Hd|Tail]) -> 904 {Before, Res, After, Accu1} = 905 case F(Hd, Accu0) of 906 {Be, _, Af, _} = Result when is_list(Be), is_list(Af) -> 907 Result; 908 {R1, A1} -> 909 {[], R1, [], A1} 910 end, 911 {Rs, Accu2} = mapfoldl(F, Accu1, Tail), 912 {Before ++ [Res| After ++ Rs], Accu2}; 913mapfoldl(F, Accu, []) when is_function(F, 2) -> {[], Accu}. 914 915 916rpt_error(_Reason, _Fun, _Info, _Trace) -> 917 %% Fmt = lists:flatten( 918 %% ["*** ERROR in parse_transform function:~n" 919 %% "*** Reason = ~p~n", 920 %% "*** Location: ~p~n", 921 %% "*** Trace: ~p~n", 922 %% ["*** ~10w = ~p~n" || _ <- Info]]), 923 %% Args = [Reason, Fun, Trace | 924 %% lists:foldr( 925 %% fun({K,V}, Acc) -> 926 %% [K, V | Acc] 927 %% end, [], Info)], 928 %%io:format(Fmt, Args), 929 ok. 930 931-spec format_error({atom(), term()}) -> 932 iolist(). 933format_error({E, [{M,F,A}|_]} = Error) -> 934 try lists:flatten(io_lib:fwrite("~p in ~s:~s/~s", [E, atom_to_list(M), 935 atom_to_list(F), integer_to_list(A)])) 936 catch 937 error:_ -> 938 format_error_(Error) 939 end; 940format_error(Error) -> 941 format_error_(Error). 942 943format_error_(Error) -> 944 lists:flatten(io_lib:fwrite("~p", [Error])). 945