diff options
-rw-r--r-- | erts/doc/src/absform.xml | 5 | ||||
-rw-r--r-- | erts/doc/src/erlang.xml | 25 | ||||
-rw-r--r-- | erts/emulator/beam/bif.c | 13 | ||||
-rw-r--r-- | erts/emulator/beam/erl_map.c | 39 | ||||
-rw-r--r-- | erts/emulator/beam/erl_map.h | 4 | ||||
-rw-r--r-- | erts/emulator/beam/erl_proc_sig_queue.c | 4 | ||||
-rw-r--r-- | erts/emulator/test/process_SUITE.erl | 16 | ||||
-rw-r--r-- | erts/preloaded/src/erlang.erl | 3 | ||||
-rw-r--r-- | lib/compiler/doc/src/compile.xml | 2 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 65 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 58 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE_data/column_pt.erl | 63 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE_data/line_pt.erl | 16 | ||||
-rw-r--r-- | lib/edoc/src/edoc.erl | 3 | ||||
-rw-r--r-- | lib/edoc/src/edoc.hrl | 3 | ||||
-rw-r--r-- | lib/edoc/src/edoc_extract.erl | 15 | ||||
-rw-r--r-- | lib/stdlib/examples/erl_id_trans.erl | 2 | ||||
-rw-r--r-- | lib/stdlib/test/maps_SUITE.erl | 8 |
18 files changed, 270 insertions, 74 deletions
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 5359a41d37..1d98dce19d 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -195,11 +195,14 @@ The word <c>LOCATION</c> represents a location, and denotes the number of the last line, and possibly the number of the last column on that line, in the source file. See <seeerl - marker="stdlib:erl_anno">erl_anno(3)</seeerl> for + marker="stdlib:erl_anno"><c>erl_anno(3)</c></seeerl> for details. </p> </item> </list> + <p>See <seetype marker="stdlib:erl_parse#form_info"> + <c>the form_info/0</c></seetype> type in + <c>erl_parse(3)</c> for more details about these values.</p> </section> </section> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 512a40305d..f69bc9c1d8 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5596,7 +5596,18 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="3" + <name name="process_flag" arity="2" clause_i="3" since="OTP @OTP-17285@"/> + <fsummary>Set process flag fullsweep_after for the calling process. + </fsummary> + <desc> + <p>Changes the maximum number of generational collections + before forcing a fullsweep for the calling process.</p> + <p>Returns the old value of the flag.</p> + </desc> + </func> + + <func> + <name name="process_flag" arity="2" clause_i="4" anchor="process_flag_min_heap_size" since=""/> <fsummary>Set process flag min_heap_size for the calling process. </fsummary> @@ -5607,7 +5618,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="4" since="OTP R13B04"/> + <name name="process_flag" arity="2" clause_i="5" since="OTP R13B04"/> <fsummary>Set process flag min_bin_vheap_size for the calling process. </fsummary> <desc> @@ -5618,7 +5629,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="5" + <name name="process_flag" arity="2" clause_i="6" anchor="process_flag_max_heap_size" since="OTP 19.0"/> <fsummary>Set process flag max_heap_size for the calling process. </fsummary> @@ -5692,7 +5703,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="6" + <name name="process_flag" arity="2" clause_i="7" anchor="process_flag_message_queue_data" since="OTP 19.0"/> <fsummary>Set process flag message_queue_data for the calling process. </fsummary> @@ -5734,7 +5745,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="7" + <name name="process_flag" arity="2" clause_i="8" anchor="process_flag_priority" since=""/> <fsummary>Set process flag priority for the calling process.</fsummary> <type name="priority_level"/> @@ -5807,7 +5818,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="8" since=""/> + <name name="process_flag" arity="2" clause_i="9" since=""/> <fsummary>Set process flag save_calls for the calling process.</fsummary> <desc> <p><c><anno>N</anno></c> must be an integer in the interval 0..10000. @@ -5838,7 +5849,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="9" since=""/> + <name name="process_flag" arity="2" clause_i="10" since=""/> <fsummary>Set process flag sensitive for the calling process.</fsummary> <desc> <p>Sets or clears flag <c>sensitive</c> for the current process. diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index af3437c919..d834939cab 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1854,6 +1854,19 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) else BIF_RET(old_value); } + else if (BIF_ARG_1 == am_fullsweep_after) { + Sint i; + if (!is_small(BIF_ARG_2)) { + goto error; + } + i = signed_val(BIF_ARG_2); + if (i < 0) { + goto error; + } + old_value = make_small(BIF_P->max_gen_gcs); + BIF_P->max_gen_gcs = i; + BIF_RET(old_value); + } else if (BIF_ARG_1 == am_min_heap_size) { Sint i; if (!is_small(BIF_ARG_2)) { diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 9b1762e299..4f9d9ce287 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -2370,39 +2370,53 @@ Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Uint size, upsz; Eterm *hp, res = THE_NON_VALUE; DECLARE_ESTACK(stack); - if (erts_hashmap_insert_down(hx, key, map, &size, &upsz, &stack, is_update)) { - hp = HAlloc(p, size); - res = erts_hashmap_insert_up(hp, key, value, &upsz, &stack); + if (erts_hashmap_insert_down(hx, key, value, map, &size, &upsz, &stack, + is_update)) { + if (size) { + /* We are putting a new value (under a new or existing key) */ + hp = HAlloc(p, size); + res = erts_hashmap_insert_up(hp, key, value, upsz, &stack); + } + else { + /* We are putting the same key-value */ + res = map; + } + } + else { + /* We are updating and the key does not exist */ + ASSERT(is_update); } - DESTROY_ESTACK(stack); + DESTROY_ESTACK(stack); return res; } -int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, +int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm value, Eterm node, Uint *sz, Uint *update_size, ErtsEStack *sp, int is_update) { Eterm *ptr; Eterm hdr, ckey; Uint32 ix, cix, bp, hval, chx; Uint slot, lvl = 0, clvl; Uint size = 0, n = 0; - DeclareTmpHeapNoproc(th,2); + Eterm th[2]; *update_size = 1; - UseTmpHeapNoproc(2); for (;;) { switch(primary_tag(node)) { case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ ptr = list_val(node); ckey = CAR(ptr); if (EQ(ckey, key)) { + if (CDR(ptr) == value) { + *sz = 0; /* same value, same map, no heap needed */ + return 1; + } *update_size = 0; goto unroll; } if (is_update) { - UnUseTmpHeapNoproc(2); return 0; } goto insert_subnodes; @@ -2435,7 +2449,6 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, if (!(bp & hval)) { /* not occupied */ if (is_update) { - UnUseTmpHeapNoproc(2); return 0; } size += HAMT_NODE_BITMAP_SZ(n+1); @@ -2467,7 +2480,6 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, } /* not occupied */ if (is_update) { - UnUseTmpHeapNoproc(2); return 0; } size += HAMT_HEAD_BITMAP_SZ(n+1); @@ -2501,12 +2513,11 @@ insert_subnodes: unroll: *sz = size + /* res cons */ 2; - UnUseTmpHeapNoproc(2); return 1; } Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, - Uint *update_size, ErtsEStack *sp) { + Uint update_size, ErtsEStack *sp) { Eterm node, *ptr, hdr; Eterm res; Eterm *nhp = NULL; @@ -2551,7 +2562,7 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, nhp = hp; n = HAMT_HEAD_ARRAY_SZ - 2; *hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++; - *hp++ = (*ptr++) + *update_size; + *hp++ = (*ptr++) + update_size; while(n--) { *hp++ = *ptr++; } nhp[slot+2] = res; res = make_hashmap(nhp); @@ -2579,7 +2590,7 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, hval = MAP_HEADER_VAL(hdr); nhp = hp; *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++; - *hp++ = (*ptr++) + *update_size; + *hp++ = (*ptr++) + update_size; n -= slot; while(slot--) { *hp++ = *ptr++; } diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 718d400e22..980cf246c4 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -85,10 +85,10 @@ int erts_maps_take(Process *p, Eterm key, Eterm map, Eterm *res, Eterm *value Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, Eterm node, int is_update); -int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, +int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm value, Eterm node, Uint *sz, Uint *upsz, struct ErtsEStack_ *sp, int is_update); Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, - Uint *upsz, struct ErtsEStack_ *sp); + Uint upsz, struct ErtsEStack_ *sp); int erts_validate_and_sort_flatmap(flatmap_t* map); void hashmap_iterator_init(struct ErtsWStack_* s, Eterm node, int reverse); diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index df48916e06..ac1ce1473f 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -3973,6 +3973,10 @@ handle_process_info(Process *c_p, ErtsSigRecvTracing *tracing, ASSERT(tracing); if (*next_nm_sig != &c_p->sig_qs.cont) { + if (ERTS_SIG_IS_RECV_MARKER(c_p->sig_qs.cont)) { + ErtsRecvMarker *markp = (ErtsRecvMarker *) c_p->sig_qs.cont; + markp->prev_next = c_p->sig_qs.last; + } if (*next_nm_sig == tracing->messages.next) tracing->messages.next = &c_p->sig_qs.cont; *c_p->sig_qs.last = c_p->sig_qs.cont; diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 0e39622540..bacf052f47 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -47,7 +47,8 @@ process_info_reductions/1, bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1, otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1, - process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1, + process_info_messages/1, process_flag_badarg/1, + process_flag_fullsweep_after/1, process_flag_heap_size/1, spawn_opt_heap_size/1, spawn_opt_max_heap_size/1, processes_large_tab/1, processes_default_tab/1, processes_small_tab/1, processes_this_tab/1, processes_apply_trap/1, @@ -106,7 +107,8 @@ all() -> process_info_reductions, bump_reductions, low_prio, yield, yield2, otp_4725, bad_register, garbage_collect, process_info_messages, - process_flag_badarg, process_flag_heap_size, + process_flag_badarg, + process_flag_fullsweep_after, process_flag_heap_size, spawn_opt_heap_size, spawn_opt_max_heap_size, spawn_huge_arglist, spawn_request_bif, @@ -1595,6 +1597,7 @@ process_flag_badarg(Config) when is_list(Config) -> chk_badarg(fun () -> process_flag(gurka, banan) end), chk_badarg(fun () -> process_flag(trap_exit, gurka) end), chk_badarg(fun () -> process_flag(error_handler, 1) end), + chk_badarg(fun () -> process_flag(fullsweep_after, gurka) end), chk_badarg(fun () -> process_flag(min_heap_size, gurka) end), chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end), chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end), @@ -2213,6 +2216,15 @@ processes_gc_trap(Config) when is_list(Config) -> exit(Suspendee, bang), ok. +process_flag_fullsweep_after(Config) when is_list(Config) -> + {fullsweep_after, OldFSA} = process_info(self(), fullsweep_after), + OldFSA = process_flag(fullsweep_after, 12345), + {fullsweep_after, 12345} = process_info(self(), fullsweep_after), + 12345 = process_flag(fullsweep_after, 0), + {fullsweep_after, 0} = process_info(self(), fullsweep_after), + 0 = process_flag(fullsweep_after, OldFSA), + ok. + process_flag_heap_size(Config) when is_list(Config) -> HSize = 2586, % must be gc fib+ number VHSize = 318187, % must be gc fib+ number diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 2983966ade..e0f216ca5e 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2359,6 +2359,9 @@ open_port(PortName, PortSettings) -> (error_handler, Module) -> OldModule when Module :: atom(), OldModule :: atom(); + (fullsweep_after, FullsweepAfter) -> OldFullsweepAfter when + FullsweepAfter :: non_neg_integer(), + OldFullsweepAfter :: non_neg_integer(); (min_heap_size, MinHeapSize) -> OldMinHeapSize when MinHeapSize :: non_neg_integer(), OldMinHeapSize :: non_neg_integer(); diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 26555438b8..8906b1c2eb 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -363,7 +363,7 @@ module.beam: module.erl \ since R13B04.</p> </item> - <tag><c>error_location</c></tag> + <tag><c>{error_location,line | column}</c></tag> <item> <p>If the value of this flag is <c>line</c>, the location <seeerl marker="#error_information"><c>ErrorLocation</c></seeerl> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index c9b902d8ff..515f5facd8 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -1049,7 +1049,10 @@ do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) -> end. with_columns(Opts) -> - proplists:get_value(error_location, Opts, column) =:= column. + case proplists:get_value(error_location, Opts, column) of + column -> true; + line -> false + end. consult_abstr(_Code, St) -> case file:consult(St#compile.ifile) of @@ -1138,16 +1141,15 @@ foldl_transform([T|Ts], Code0, St) -> T:parse_transform(Code, S#compile.options) end, Run = runner(none, St), - try Run({Name, Fun}, Code0, St) of + StrippedCode = maybe_strip_columns(Code0, T, St), + try Run({Name, Fun}, StrippedCode, St) of {error,Es,Ws} -> {error,St#compile{warnings=St#compile.warnings ++ Ws, errors=St#compile.errors ++ Es}}; - {warning, Forms0, Ws} -> - Forms = maybe_strip_columns(Forms0, T), + {warning, Forms, Ws} -> foldl_transform(Ts, Forms, St#compile{warnings=St#compile.warnings ++ Ws}); - Forms0 -> - Forms = maybe_strip_columns(Forms0, T), + Forms -> foldl_transform(Ts, Forms, St) catch error:Reason:Stk -> @@ -1160,28 +1162,43 @@ foldl_transform([T|Ts], Code0, St) -> {undef_parse_transform,T}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end; -foldl_transform([], Code, St) -> {ok,Code,St}. - -%%% It is possible--although unlikely--that parse transforms add -%%% columns to the abstract code, why this function is called for -%%% every parse transform. -maybe_strip_columns(Code, T) -> - case erlang:function_exported(T, parse_transform_info, 0) of - true -> - Info = T:parse_transform_info(), - case maps:get(error_location, Info, column) of - line -> - strip_columns(Code); - _ -> - Code - end; - false -> - Code +foldl_transform([], Code, St) -> + %% We may need to strip columns added by parse transforms before returning + %% them back to the compiler. We pass ?MODULE as a bit of a hack to get the + %% correct default. + {ok, maybe_strip_columns(Code, ?MODULE, St), St}. + +%%% If a parse transform does not support column numbers it can say so using +%%% the parse_transform_info callback. The error_location is taken from both +%%% compiler options and from the parse transform and if either of them want +%%% to only use lines, we strip columns. +maybe_strip_columns(Code, T, St) -> + PTErrorLocation = + case erlang:function_exported(T, parse_transform_info, 0) of + true -> + maps:get(error_location, T:parse_transform_info(), column); + false -> + column + end, + ConfigErrorLocation = proplists:get_value(error_location, St#compile.options, column), + if + PTErrorLocation =:= line; ConfigErrorLocation =:= line -> + strip_columns(Code); + true -> Code end. strip_columns(Code) -> F = fun(A) -> erl_anno:set_location(erl_anno:line(A), A) end, - [erl_parse:map_anno(F, Form) || Form <- Code]. + [case Form of + {eof,{Line,_Col}} -> + {eof,Line}; + {ErrorOrWarning,{{Line,_Col},Module,Reason}} + when ErrorOrWarning =:= error; + ErrorOrWarning =:= warning -> + {ErrorOrWarning,{Line,Module,Reason}}; + Form -> + erl_parse:map_anno(F, Form) + end || Form <- Code]. get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts]. diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index e3f03d4959..7c47d8a289 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -1724,11 +1724,44 @@ do_transforms(Config) -> %% Compile a file using line_pt and verify that column numbers %% have been stripped. Big = filename:join(DataDir, "big"), - {[],[_|_]} = compile_partition_warnings(Big, line_pt), + {[],[_|_]} = compile_partition_warnings(Big, [{parse_transform,line_pt}]), %% Compile a file using column_pt and verify that column numbers %% have NOT been stripped. - {[_|_],[]} = compile_partition_warnings(Big, column_pt), + {[_|_],[]} = compile_partition_warnings(Big, [{parse_transform,column_pt}]), + + %% Compile a file using column_pt and error_location=line and verify + %% that column numbers have been stripped. + {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line}, + {parse_transform,column_pt}]), + + %% Compile a file using column_pt, line_pt and verify + %% that column numbers have been stripped. + {[],[_|_]} = compile_partition_warnings(Big, [{parse_transform,column_pt}, + {parse_transform,line_pt}]), + + %% Compile a file using column_pt that adds columns and error_location=line and + %% verify that column numbers have been stripped. + {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line}, + add_columns, + {parse_transform,column_pt}]), + + %% Compile a file using column_pt that adds columns and error_location=line and + %% then call column_pt again to check that columns are stripped in between calls. + %% and then verify that column numbers have been stripped from output. + {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line}, + add_columns, + {parse_transform,column_pt}, + {parse_transform,column_pt}]), + + %% Compile a file using column_pt that adds columns and en error and error_location=line and + %% verify that column numbers have been stripped. + {error,[{_What,[{Line,_,_}]}],[_|_]} = + compile_partition_warnings(Big, [{error_location,line}, + add_columns, + add_error, + {parse_transform,column_pt}]), + true = is_integer(Line), %% Cover transform code implementing the `time` option. {ok,big,_} = compile:file(Big, [binary, time, report, @@ -1754,15 +1787,18 @@ do_transforms(Config) -> ok. -compile_partition_warnings(Source, PT) -> - Opts = [binary, return, {core_transform,generic_pt}, {parse_transform,PT}], - {ok,big,<<_/binary>>,Ws0} = compile:file(Source, Opts), - [{_SourcePath,Ws}] = Ws0, - - %% Return {[ColumnWarning], [LineWarning]}. - lists:partition(fun({{L,C},_,_}) when is_integer(L), is_integer(C) -> true; - ({L,_,_}) when is_integer(L) -> false - end, Ws). +compile_partition_warnings(Source, Opts) -> + case compile:file(Source, [binary, return | Opts]) of + {ok,big,<<_/binary>>,Ws0} -> + [{_SourcePath,Ws}] = Ws0, + + %% Return {[ColumnWarning], [LineWarning]}. + lists:partition(fun({{L,C},_,_}) when is_integer(L), is_integer(C) -> true; + ({L,_,_}) when is_integer(L) -> false + end, Ws); + Error -> + Error + end. %% Cover the erl_compile API used by erlc. erl_compile_api(Config) -> diff --git a/lib/compiler/test/compile_SUITE_data/column_pt.erl b/lib/compiler/test/compile_SUITE_data/column_pt.erl index 93d56cb5ed..bf599bb38f 100644 --- a/lib/compiler/test/compile_SUITE_data/column_pt.erl +++ b/lib/compiler/test/compile_SUITE_data/column_pt.erl @@ -19,11 +19,68 @@ %% -module(column_pt). --export([parse_transform/2, parse_transform_info/0]). +-export([parse_transform/2, parse_transform_info/0, format_error/1]). parse_transform_info() -> %% Will default to {error_location,column}. #{}. -parse_transform(Forms, _Options) -> - Forms. +parse_transform(Forms, Options) -> + + HasColumn = + case proplists:get_value(error_location, Options, column) of + column -> + true; + line -> + false + end, + + AddColumns = proplists:get_value(add_columns, Options, false), + AddError = proplists:get_value(add_error, Options, false), + + {eof,LastLocation} = lists:keyfind(eof, 1, Forms), + LastLine = erl_anno:line(erl_anno:new(LastLocation)), + ExtraFormsAnno = + if HasColumn -> + erl_anno:new({LastLine,1}); + not HasColumn -> + erl_anno:new(LastLine) + end, + + ExtraForms = [{warning,{ExtraFormsAnno,?MODULE,"injected warning"}}] ++ + [{error,{ExtraFormsAnno,?MODULE,"injected error"}} || AddError], + + lists:map( + fun + ({eof,Location}) -> + Anno = erl_anno:new(Location), + HasColumn = erl_anno:column(Anno) =/= undefined, + if AddColumns -> + {eof,{erl_anno:line(Anno), 1}}; + not AddColumns -> + {eof,Location} + end; + ({ErrorOrWarning,{Location,Module,Text}}) + when ErrorOrWarning =:= error; + ErrorOrWarning =:= warning -> + Anno = erl_anno:new(Location), + HasColumn = erl_anno:column(Anno) =/= undefined, + if AddColumns -> + {ErrorOrWarning,{{erl_anno:line(Anno), 1}, Module, Text}}; + not AddColumns -> + {ErrorOrWarning, {Location, Module, Text}} + end; + (Form) -> + erl_parse:map_anno( + fun(Anno) -> + HasColumn = erl_anno:column(Anno) =/= undefined, + if AddColumns -> + erl_anno:set_location({erl_anno:line(Anno), 1}, Anno); + not AddColumns -> + Anno + end + end, Form) + end, Forms ++ ExtraForms). + +format_error(Error) -> + Error. diff --git a/lib/compiler/test/compile_SUITE_data/line_pt.erl b/lib/compiler/test/compile_SUITE_data/line_pt.erl index 0e77072d4c..32078081d2 100644 --- a/lib/compiler/test/compile_SUITE_data/line_pt.erl +++ b/lib/compiler/test/compile_SUITE_data/line_pt.erl @@ -24,5 +24,17 @@ parse_transform_info() -> #{error_location => line}. -parse_transform(Forms, _Options) -> - Forms. +parse_transform(Forms,_Options) -> + lists:map( + fun + ({eof,Location}) -> + Anno = erl_anno:new(Location), + false = erl_anno:column(Anno) =/= undefined, + {eof,Location}; + (Form) -> + erl_parse:map_anno( + fun(Anno) -> + false = erl_anno:column(Anno) =/= undefined, + Anno + end, Form) + end, Forms). diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl index a811037b5f..6ec5565fb0 100644 --- a/lib/edoc/src/edoc.erl +++ b/lib/edoc/src/edoc.erl @@ -77,7 +77,8 @@ exports :: ordset(function_name()), attributes :: ordset({atom(), term()}), records :: [{atom(), [{atom(), term()}]}], - encoding :: epp:source_encoding()}. + encoding :: epp:source_encoding(), + file :: file:filename()}. %% Module information. -type env() :: #env{}. diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl index 9365d2ea5b..82d1fe932f 100644 --- a/lib/edoc/src/edoc.hrl +++ b/lib/edoc/src/edoc.hrl @@ -50,7 +50,8 @@ exports = [], attributes = [], records = [], - encoding = latin1}). + encoding = latin1, + file}). -record(env, {module = [], root = "", diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl index 75df6637ab..cef5e55703 100644 --- a/lib/edoc/src/edoc_extract.erl +++ b/lib/edoc/src/edoc_extract.erl @@ -328,7 +328,8 @@ get_module_info(Forms, File) -> exports = ordsets:intersection(Exports, Functions), attributes = Attributes, records = Records, - encoding = Encoding}. + encoding = Encoding, + file = File}. get_list_keyval(Key, L) -> case lists:keyfind(Key, 1, L) of @@ -483,9 +484,19 @@ insert_specs(As, Ss, Mod) -> Specs = maps:from_list(SpecList), %% Assert that we've not skipped redundant specs for the same {Fun, Arity}. %% This should never happen, as such a module would not compile. - true = length(SpecList) == maps:size(Specs), + case length(SpecList) == maps:size(Specs) of + true -> ok; + false -> error_redundant_specs(Mod, SpecList, Specs) + end, insert_specs_(ModName, As, Specs). +error_redundant_specs(Mod, SpecList, Specs) -> + [{RedundantMFA, [Form]} | _] = lists:sort(SpecList) -- lists:sort(maps:to_list(Specs)), + {_, Line, _, _} = erl_syntax:revert(Form), + {_, F, A} = RedundantMFA, + edoc_report:error(Line, {Mod#module.file, {F, A}}, "Redundant -spec attribute found. Try setting {preprocess, true}."), + erlang:exit({redundant_spec, RedundantMFA}). + insert_specs_(_, [], _) -> []; insert_specs_(ModName, [#entry{} = A | As], Specs) -> #entry{name = {F, Arity}, data = {Cs, Cbs, _, Ts, Rs}} = A, diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl index 264148eaf4..292c06506a 100644 --- a/lib/stdlib/examples/erl_id_trans.erl +++ b/lib/stdlib/examples/erl_id_trans.erl @@ -31,7 +31,7 @@ parse_transform(Forms, _Options) -> forms(Forms). parse_transform_info() -> - #{columns => true}. + #{error_location => column}. %% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs). diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl index 4159ea6c3b..4860e39b70 100644 --- a/lib/stdlib/test/maps_SUITE.erl +++ b/lib/stdlib/test/maps_SUITE.erl @@ -495,8 +495,12 @@ iter_kv(I) -> t_put_opt(Config) when is_list(Config) -> Value = id(#{complex => map}), - Map = id(#{a => Value}), - true = erts_debug:same(maps:put(a, Value, Map), Map), + Small = id(#{a => Value}), + true = erts_debug:same(maps:put(a, Value, Small), Small), + + LargeBase = maps:from_list([{I,I}||I<-lists:seq(1,200)]), + Large = LargeBase#{a => Value}, + true = erts_debug:same(maps:put(a, Value, Large), Large), ok. t_merge_opt(Config) when is_list(Config) -> |