File 0140-syntax_tools-Annotate-map-comprehensions-and-generat.patch of Package erlang
From eddf87964f30404bc5ead54abf716775a8201cbe Mon Sep 17 00:00:00 2001
From: Isabell Huang <isabell@erlang.org>
Date: Tue, 21 Oct 2025 10:37:21 +0200
Subject: [PATCH] syntax_tools: Annotate map comprehensions and generators
Fix https://github.com/erlang/otp/issues/10119.
This fix works for 28.
---
lib/syntax_tools/src/erl_syntax_lib.erl | 93 +++++---------------
lib/syntax_tools/test/syntax_tools_SUITE.erl | 17 +++-
2 files changed, 37 insertions(+), 73 deletions(-)
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 2c4e10061f..66559173a5 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -508,6 +508,8 @@ vann(Tree, Env) ->
vann_list_comp(Tree, Env);
binary_comp ->
vann_binary_comp(Tree, Env);
+ map_comp ->
+ vann_map_comp(Tree, Env);
generator ->
vann_generator(Tree, Env);
strict_generator ->
@@ -675,7 +677,7 @@ vann_receive_expr(Tree, Env) ->
vann_list_comp(Tree, Env) ->
Es = erl_syntax:list_comp_body(Tree),
- {Es1, {Bound1, Free1}} = vann_list_comp_body(Es, Env),
+ {Es1, {Bound1, Free1}} = vann_comp_body(Es, Env),
Env1 = ordsets:union(Env, Bound1),
T = erl_syntax:list_comp_template(Tree),
{T1, _, Free2} = vann(T, Env1),
@@ -684,44 +686,9 @@ vann_list_comp(Tree, Env) ->
Tree1 = rewrite(Tree, erl_syntax:list_comp(T1, Es1)),
{ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
-vann_list_comp_body_join() ->
- fun (T, {Env, Bound, Free}) ->
- {T1, Bound1, Free1} = case erl_syntax:type(T) of
- generator ->
- vann_generator(T, Env);
- strict_generator ->
- vann_strict_generator(T, Env);
- binary_generator ->
- vann_binary_generator(T,Env);
- strict_binary_generator ->
- vann_strict_binary_generator(T,Env);
- map_generator ->
- vann_map_generator(T,Env);
- strict_map_generator ->
- vann_strict_map_generator(T,Env);
- zip_generator ->
- vann_zip_generator(T,Env);
- _ ->
- %% Bindings in filters are not
- %% exported to the rest of the
- %% body.
- {T2, _, Free2} = vann(T, Env),
- {T2, [], Free2}
- end,
- Env1 = ordsets:union(Env, Bound1),
- {T1, {Env1, ordsets:union(Bound, Bound1),
- ordsets:union(Free,
- ordsets:subtract(Free1, Bound))}}
- end.
-
-vann_list_comp_body(Ts, Env) ->
- F = vann_list_comp_body_join(),
- {Ts1, {_, Bound, Free}} = lists:mapfoldl(F, {Env, [], []}, Ts),
- {Ts1, {Bound, Free}}.
-
vann_binary_comp(Tree, Env) ->
Es = erl_syntax:binary_comp_body(Tree),
- {Es1, {Bound1, Free1}} = vann_binary_comp_body(Es, Env),
+ {Es1, {Bound1, Free1}} = vann_comp_body(Es, Env),
Env1 = ordsets:union(Env, Bound1),
T = erl_syntax:binary_comp_template(Tree),
{T1, _, Free2} = vann(T, Env1),
@@ -730,7 +697,23 @@ vann_binary_comp(Tree, Env) ->
Tree1 = rewrite(Tree, erl_syntax:binary_comp(T1, Es1)),
{ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
-vann_binary_comp_body_join() ->
+vann_map_comp(Tree, Env) ->
+ Es = erl_syntax:map_comp_body(Tree),
+ {Es1, {Bound1, Free1}} = vann_comp_body(Es, Env),
+ Env1 = ordsets:union(Env, Bound1),
+ T = erl_syntax:map_comp_template(Tree),
+ {T1, _, Free2} = vann(T, Env1),
+ Free = ordsets:union(Free1, ordsets:subtract(Free2, Bound1)),
+ Bound = [],
+ Tree1 = rewrite(Tree, erl_syntax:map_comp(T1, Es1)),
+ {ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
+
+vann_comp_body(Ts, Env) ->
+ F = vann_comp_body_join(),
+ {Ts1, {_, Bound, Free}} = lists:mapfoldl(F, {Env, [], []}, Ts),
+ {Ts1, {Bound, Free}}.
+
+vann_comp_body_join() ->
fun (T, {Env, Bound, Free}) ->
{T1, Bound1, Free1} = case erl_syntax:type(T) of
generator ->
@@ -760,38 +743,6 @@ vann_binary_comp_body_join() ->
ordsets:subtract(Free1, Bound))}}
end.
-vann_binary_comp_body(Ts, Env) ->
- F = vann_binary_comp_body_join(),
- {Ts1, {_, Bound, Free}} = lists:mapfoldl(F, {Env, [], []}, Ts),
- {Ts1, {Bound, Free}}.
-
-vann_zip_generator_body_join() ->
- fun (T, {Env, Bound, Free}) ->
- {T1, Bound1, Free1} = case erl_syntax:type(T) of
- generator ->
- vann_generator(T, Env);
- strict_generator ->
- vann_strict_generator(T, Env);
- binary_generator ->
- vann_binary_generator(T,Env);
- strict_binary_generator ->
- vann_strict_binary_generator(T,Env);
- map_generator ->
- vann_map_generator(T,Env);
- strict_map_generator ->
- vann_strict_map_generator(T,Env)
- end,
- Env1 = ordsets:union(Env, Bound1),
- {T1, {Env1, ordsets:union(Bound, Bound1),
- ordsets:union(Free,
- ordsets:subtract(Free1, Bound))}}
- end.
-
-vann_zip_generator_body(Ts, Env) ->
- F = vann_zip_generator_body_join(),
- {Ts1, {_, Bound, Free}} = lists:mapfoldl(F, {Env, [], []}, Ts),
- {Ts1, {Bound, Free}}.
-
%% In list comprehension generators, the pattern variables are always
%% viewed as new occurrences, shadowing whatever is in the input
%% environment (thus, the pattern contains no variable uses, only
@@ -847,7 +798,7 @@ vann_strict_map_generator(Tree, Env) ->
vann_zip_generator(Tree, Env) ->
Es = erl_syntax:zip_generator_body(Tree),
- {Es1, {Bound, Free}} = vann_zip_generator_body(Es, Env),
+ {Es1, {Bound, Free}} = vann_comp_body(Es, Env),
Env1 = ordsets:union(Env, Bound),
Tree1 = rewrite(Tree, erl_syntax:zip_generator(Es1)),
{ann_bindings(Tree1, Env1, Bound, Free), Bound, Free}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index 32bf13f86b..43eb7f011d 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -43,7 +43,7 @@
t_abstract_type/1,t_erl_parse_type/1,t_type/1,
t_epp_dodger/1,t_epp_dodger_clever/1,
t_comment_scan/1,t_prettypr/1,test_named_fun_bind_ann/1,
- test_maybe_expr_ann/1,test_zip_ann/1]).
+ test_maybe_expr_ann/1,test_mc_ann/1,test_zip_ann/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -54,7 +54,7 @@ all() ->
t_abstract_type,t_erl_parse_type,t_type,
t_epp_dodger,t_epp_dodger_clever,
t_comment_scan,t_prettypr,test_named_fun_bind_ann,
- test_maybe_expr_ann,test_zip_ann].
+ test_maybe_expr_ann,test_mc_ann,test_zip_ann].
groups() ->
[].
@@ -492,6 +492,19 @@ test_maybe_expr_ann(Config) when is_list(Config) ->
ok.
+test_mc_ann(Config) when is_list(Config) ->
+ Expr = {mc,1,
+ {map_field_assoc,1,{var,1,'X'},{var,1,'Y'}},
+ [{generate,1,
+ {tuple,1,[{var,1,'X'},{var,1,'Y'}]},
+ {var,1,'Pairs'}}]},
+ ZipAnn = erl_syntax_lib:annotate_bindings(Expr, []),
+ [Env, Bound, Free] = erl_syntax:get_ann(ZipAnn),
+ {'env',[]} = Env,
+ {'bound',[]} = Bound,
+ {'free',['Pairs']} = Free,
+ ok.
+
test_zip_ann(Config) when is_list(Config) ->
Expr = {lc,1,
{tuple,1,[{var,1,'A'},{var,1,'B'}]},
--
2.51.0