aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/sys_core_fold.erl4
-rw-r--r--lib/compiler/src/v3_core.erl3
-rw-r--r--lib/compiler/test/warnings_SUITE.erl9
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl26
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl257
5 files changed, 270 insertions, 29 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index d1eec9e347..9ce6bb48bf 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -305,6 +305,10 @@ expr(#c_let{}=Let, Ctxt, Sub) ->
%% Now recursively re-process the new expression.
expr(Expr, Ctxt, sub_new_preserve_types(Sub))
end;
+expr(#c_letrec{body=#c_var{}}=Letrec, effect, _Sub) ->
+ %% This is named fun in an 'effect' context. Warn and ignore.
+ add_warning(Letrec, useless_building),
+ void();
expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) ->
Fs1 = map(fun ({Name,Fb}) ->
{Name,expr(Fb, {letrec,Ctxt}, Sub)}
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index e30bfa729c..59863f52ec 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -2088,7 +2088,8 @@ cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0,
RecVar = #c_var{name={Name,length(Ps)}},
Let = #c_let{vars=[#c_var{name=Name}],arg=RecVar,body=Body},
CFun1 = CFun0#c_fun{body=Let},
- Letrec = #c_letrec{defs=[{RecVar,CFun1}],
+ Letrec = #c_letrec{anno=A0#a.anno,
+ defs=[{RecVar,CFun1}],
body=RecVar},
{Letrec,[],Us1,St1}
end;
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 7186956603..16d15a59e5 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -390,6 +390,10 @@ effect(Config) when is_list(Config) ->
<<X:8>>;
unused_fun ->
fun() -> {ok,X} end;
+ unused_named_fun ->
+ fun F(0) -> 1;
+ F(N) -> N*F(N-1)
+ end;
unused_atom ->
ignore; %no warning
unused_nil ->
@@ -484,8 +488,9 @@ effect(Config) when is_list(Config) ->
{22,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
{24,sys_core_fold,useless_building},
{26,sys_core_fold,useless_building},
- {32,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
- {34,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
+ {28,sys_core_fold,useless_building},
+ {36,sys_core_fold,{no_effect,{erlang,'=:=',2}}},
+ {38,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}],
?line [] = run(Config, Ts),
ok.
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index 1ffcf31134..4d6e88f58b 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -892,6 +892,32 @@ lay_2(Node, Ctxt) ->
beside(floating(text(".")), D2)),
maybe_parentheses(D3, Prec, Ctxt);
+ map_expr ->
+ {PrecL, Prec, _} = inop_prec('#'),
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = par(seq(erl_syntax:map_expr_fields(Node),
+ floating(text(",")), Ctxt1, fun lay/2)),
+ D2 = beside(text("#{"), beside(D1, floating(text("}")))),
+ D3 = case erl_syntax:map_expr_argument(Node) of
+ none ->
+ D2;
+ A ->
+ beside(lay(A, set_prec(Ctxt, PrecL)), D2)
+ end,
+ maybe_parentheses(D3, Prec, Ctxt);
+
+ map_field_assoc ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_field_assoc_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_field_assoc_value(Node), Ctxt1),
+ par([D1, floating(text("=>")), D2], Ctxt1#ctxt.break_indent);
+
+ map_field_exact ->
+ Ctxt1 = reset_prec(Ctxt),
+ D1 = lay(erl_syntax:map_field_exact_name(Node), Ctxt1),
+ D2 = lay(erl_syntax:map_field_exact_value(Node), Ctxt1),
+ par([D1, floating(text(":=")), D2], Ctxt1#ctxt.break_indent);
+
rule ->
%% Comments on the name will be repeated; cf.
%% `function'.
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 93187fa018..2f4c9ac309 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -220,6 +220,16 @@
macro/2,
macro_arguments/1,
macro_name/1,
+ map_expr/1,
+ map_expr/2,
+ map_expr_argument/1,
+ map_expr_fields/1,
+ map_field_assoc/2,
+ map_field_assoc_name/1,
+ map_field_assoc_value/1,
+ map_field_exact/2,
+ map_field_exact_name/1,
+ map_field_exact_value/1,
match_expr/2,
match_expr_body/1,
match_expr_pattern/1,
@@ -580,11 +590,12 @@ type(Node) ->
{lc, _, _, _} -> list_comp;
{bc, _, _, _} -> binary_comp;
{match, _, _, _} -> match_expr;
+ {map, _, _, _} -> map_expr;
+ {map, _, _} -> map_expr;
+ {map_field_assoc, _, _, _} -> map_field_assoc;
+ {map_field_exact, _, _, _} -> map_field_exact;
{op, _, _, _, _} -> infix_expr;
{op, _, _, _} -> prefix_expr;
- {map,_,_} -> map;
- {map_field_assoc,_,_,_} -> map_field_assoc;
- {map_field_exact,_,_,_} -> map_field_exact;
{record, _, _, _, _} -> record_expr;
{record, _, _, _} -> record_expr;
{record_field, _, _, _, _} -> record_access;
@@ -1913,26 +1924,206 @@ atom_literal(Node) ->
%% =====================================================================
+%% @equiv map_expr(none, Fields)
-map_elements(Node) ->
+-spec map_expr([syntaxTree()]) -> syntaxTree().
+
+map_expr(Fields) ->
+ map_expr(none, Fields).
+
+
+%% =====================================================================
+%% @doc Creates an abstract map expression. If `Fields' is
+%% `[F1, ..., Fn]', then if `Argument' is `none', the result represents
+%% "<code>#{<em>F1</em>, ..., <em>Fn</em>}</code>",
+%% otherwise it represents
+%% "<code><em>Argument</em>#{<em>F1</em>, ..., <em>Fn</em>}</code>".
+%%
+%% @see map_expr/1
+%% @see map_expr_argument/1
+%% @see map_expr_fields/1
+%% @see map_field_assoc/2
+%% @see map_field_exact/2
+
+-record(map_expr, {argument :: 'none' | syntaxTree(),
+ fields :: [syntaxTree()]}).
+
+%% `erl_parse' representation:
+%%
+%% {map, Pos, Fields}
+%% {map, Pos, Argument, Fields}
+
+-spec map_expr('none' | syntaxTree(), [syntaxTree()]) -> syntaxTree().
+
+map_expr(Argument, Fields) ->
+ tree(map_expr, #map_expr{argument = Argument, fields = Fields}).
+
+revert_map_expr(Node) ->
+ Pos = get_pos(Node),
+ Argument = map_expr_argument(Node),
+ Fields = map_expr_fields(Node),
+ case Argument of
+ none ->
+ {map, Pos, Fields};
+ _ ->
+ {map, Pos, Argument, Fields}
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the argument subtree of a `map_expr' node, if any. If `Node'
+%% represents "<code>#{...}</code>", `none' is returned.
+%% Otherwise, if `Node' represents "<code><em>Argument</em>#{...}</code>",
+%% `Argument' is returned.
+%%
+%% @see map_expr/3
+
+-spec map_expr_argument(syntaxTree()) -> 'none' | syntaxTree().
+
+map_expr_argument(Node) ->
case unwrap(Node) of
- {map, _, List} ->
- List;
- Node1 ->
- data(Node1)
+ {map, _, _} ->
+ none;
+ {map, _, Argument, _} ->
+ Argument;
+ Node1 ->
+ (data(Node1))#map_expr.argument
end.
-map_field_elements({_,_,K,V}) ->
- [K,V].
-map(List) ->
- tree(map, List).
+%% =====================================================================
+%% @doc Returns the list of field subtrees of a `map_expr' node.
+%%
+%% @see map_expr/3
+
+-spec map_expr_fields(syntaxTree()) -> [syntaxTree()].
+
+map_expr_fields(Node) ->
+ case unwrap(Node) of
+ {map, _, Fields} ->
+ Fields;
+ {map, _, _, Fields} ->
+ Fields;
+ Node1 ->
+ (data(Node1))#map_expr.fields
+ end.
+
-map_field_assoc(List) ->
- tree(map_field_assoc, List).
+%% =====================================================================
+%% @doc Creates an abstract map assoc field. The result represents
+%% "<code><em>Name</em> => <em>Value</em></code>".
+%%
+%% @see map_field_assoc_name/1
+%% @see map_field_assoc_value/1
+%% @see map_expr/3
+
+-record(map_field_assoc, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {map_field_assoc, Pos, Name, Value}
+
+-spec map_field_assoc(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_field_assoc(Name, Value) ->
+ tree(map_field_assoc, #map_field_assoc{name = Name, value = Value}).
+
+revert_map_field_assoc(Node) ->
+ Pos = get_pos(Node),
+ Name = map_field_assoc_name(Node),
+ Value = map_field_assoc_value(Node),
+ {map_field_assoc, Pos, Name, Value}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_field_assoc' node.
+%%
+%% @see map_field_assoc/2
+
+-spec map_field_assoc_name(syntaxTree()) -> syntaxTree().
+
+map_field_assoc_name(Node) ->
+ case Node of
+ {map_field_assoc, _, Name, _} ->
+ Name;
+ _ ->
+ (data(Node))#map_field_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_field_assoc' node.
+%%
+%% @see map_field_assoc/2
+
+-spec map_field_assoc_value(syntaxTree()) -> syntaxTree().
+
+map_field_assoc_value(Node) ->
+ case Node of
+ {map_field_assoc, _, _, Value} ->
+ Value;
+ _ ->
+ (data(Node))#map_field_assoc.name
+ end.
+
+
+%% =====================================================================
+%% @doc Creates an abstract map exact field. The result represents
+%% "<code><em>Name</em> := <em>Value</em></code>".
+%%
+%% @see map_field_exact_name/1
+%% @see map_field_exact_value/1
+%% @see map_expr/3
+
+-record(map_field_exact, {name :: syntaxTree(), value :: syntaxTree()}).
+
+%% `erl_parse' representation:
+%%
+%% {map_field_exact, Pos, Name, Value}
+
+-spec map_field_exact(syntaxTree(), syntaxTree()) -> syntaxTree().
+
+map_field_exact(Name, Value) ->
+ tree(map_field_exact, #map_field_exact{name = Name, value = Value}).
+
+revert_map_field_exact(Node) ->
+ Pos = get_pos(Node),
+ Name = map_field_exact_name(Node),
+ Value = map_field_exact_value(Node),
+ {map_field_exact, Pos, Name, Value}.
+
+
+%% =====================================================================
+%% @doc Returns the name subtree of a `map_field_exact' node.
+%%
+%% @see map_field_exact/2
+
+-spec map_field_exact_name(syntaxTree()) -> syntaxTree().
+
+map_field_exact_name(Node) ->
+ case Node of
+ {map_field_exact, _, Name, _} ->
+ Name;
+ _ ->
+ (data(Node))#map_field_exact.name
+ end.
+
+
+%% =====================================================================
+%% @doc Returns the value subtree of a `map_field_exact' node.
+%%
+%% @see map_field_exact/2
+
+-spec map_field_exact_value(syntaxTree()) -> syntaxTree().
+
+map_field_exact_value(Node) ->
+ case Node of
+ {map_field_exact, _, _, Value} ->
+ Value;
+ _ ->
+ (data(Node))#map_field_exact.name
+ end.
-map_field_exact(List) ->
- tree(map_field_exact, List).
%% =====================================================================
%% @doc Creates an abstract tuple. If `Elements' is
@@ -6117,6 +6308,12 @@ revert_root(Node) ->
revert_list(Node);
list_comp ->
revert_list_comp(Node);
+ map_expr ->
+ revert_map_expr(Node);
+ map_field_assoc ->
+ revert_map_field_assoc(Node);
+ map_field_exact ->
+ revert_map_field_exact(Node);
match_expr ->
revert_match_expr(Node);
module_qualifier ->
@@ -6358,6 +6555,19 @@ subtrees(T) ->
As ->
[[macro_name(T)], As]
end;
+ map_expr ->
+ case map_expr_argument(T) of
+ none ->
+ [map_expr_fields(T)];
+ V ->
+ [[V], map_expr_fields(T)]
+ end;
+ map_field_assoc ->
+ [[map_field_assoc_name(T)],
+ [map_field_assoc_value(T)]];
+ map_field_exact ->
+ [[map_field_exact_name(T)],
+ [map_field_exact_value(T)]];
match_expr ->
[[match_expr_pattern(T)],
[match_expr_body(T)]];
@@ -6421,12 +6631,6 @@ subtrees(T) ->
try_expr_clauses(T),
try_expr_handlers(T),
try_expr_after(T)];
- map ->
- [map_elements(T)];
- map_field_assoc ->
- [map_field_elements(T)];
- map_field_exact ->
- [map_field_elements(T)];
tuple ->
[tuple_elements(T)]
end
@@ -6502,6 +6706,10 @@ make_tree(list, [P, [S]]) -> list(P, S);
make_tree(list_comp, [[T], B]) -> list_comp(T, B);
make_tree(macro, [[N]]) -> macro(N);
make_tree(macro, [[N], A]) -> macro(N, A);
+make_tree(map_expr, [Fs]) -> map_expr(Fs);
+make_tree(map_expr, [[E], Fs]) -> map_expr(E, Fs);
+make_tree(map_field_assoc, [[K], [V]]) -> map_field_assoc(K, V);
+make_tree(map_field_exact, [[K], [V]]) -> map_field_exact(K, V);
make_tree(match_expr, [[P], [E]]) -> match_expr(P, E);
make_tree(named_fun_expr, [[N], C]) -> named_fun_expr(N, C);
make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N);
@@ -6522,10 +6730,7 @@ make_tree(record_index_expr, [[T], [F]]) ->
make_tree(rule, [[N], C]) -> rule(N, C);
make_tree(size_qualifier, [[N], [A]]) -> size_qualifier(N, A);
make_tree(try_expr, [B, C, H, A]) -> try_expr(B, C, H, A);
-make_tree(tuple, [E]) -> tuple(E);
-make_tree(map, [E]) -> map(E);
-make_tree(map_field_assoc, [E]) -> map_field_assoc(E);
-make_tree(map_field_exact, [E]) -> map_field_exact(E).
+make_tree(tuple, [E]) -> tuple(E).
%% =====================================================================