File 0179-syntax_tools-Annotate-map-comprehensions-and-generat.patch of Package erlang

From 90f71a1046efc3115598133bcb79920da567aea7 Mon Sep 17 00:00:00 2001
From: Isabell Huang <isabell@erlang.org>
Date: Wed, 20 Aug 2025 15:03:58 +0200
Subject: [PATCH] syntax_tools: Annotate map comprehensions and generators

Fix https://github.com/erlang/otp/issues/10119. This fix works for 26
and 27.
---
 lib/syntax_tools/src/erl_syntax_lib.erl      | 72 ++++++++++----------
 lib/syntax_tools/test/syntax_tools_SUITE.erl | 18 ++++-
 2 files changed, 52 insertions(+), 38 deletions(-)

diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index b7afbc17d1..7fba55a39f 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -496,10 +496,14 @@ 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);
         binary_generator ->
             vann_binary_generator(Tree, Env);
+        map_generator ->
+            vann_map_generator(Tree, Env);
         block_expr ->
             vann_block_expr(Tree, Env);
         macro ->
@@ -653,7 +657,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),
@@ -662,34 +666,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
-                                      binary_generator ->
-				          vann_binary_generator(T,Env);
-				      generator ->
-                                          vann_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),
@@ -698,13 +677,31 @@ 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
-                                    binary_generator ->
-				          vann_binary_generator(T, Env);
-				    generator ->
+                                      binary_generator ->
+				          vann_binary_generator(T,Env);
+				      generator ->
                                           vann_generator(T, Env);
+                                      map_generator ->
+                                          vann_map_generator(T,Env);
                                       _ ->
                                           %% Bindings in filters are not
                                           %% exported to the rest of the
@@ -718,11 +715,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}}.
-
 %% 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
@@ -744,6 +736,14 @@ vann_binary_generator(Tree, Env) ->
     Tree1 = rewrite(Tree, erl_syntax:binary_generator(P1, E1)),
     {ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
 
+vann_map_generator(Tree, Env) ->
+    P = erl_syntax:map_generator_pattern(Tree),
+    {P1, Bound, _} = vann_pattern(P, []),
+    E = erl_syntax:map_generator_body(Tree),
+    {E1, _, Free} = vann(E, Env),
+    Tree1 = rewrite(Tree, erl_syntax:map_generator(P1, E1)),
+    {ann_bindings(Tree1, Env, Bound, Free), Bound, Free}.
+
 vann_block_expr(Tree, Env) ->
     Es = erl_syntax:block_expr_body(Tree),
     {Es1, {Bound, Free}} = vann_body(Es, Env),
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index fe67dd7b66..5f9ca8487a 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -29,7 +29,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_maybe_expr_ann/1,test_mc_ann/1]).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
@@ -40,7 +40,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_maybe_expr_ann,test_mc_ann].
 
 groups() -> 
     [].
@@ -337,6 +337,8 @@ t_erl_parse_type(Config) when is_list(Config) ->
 		     {"[catch V||V <- Vs]", list_comp,false},
 		     {"<< <<B>> || <<B>> <= Bs>>", binary_comp,false},
 		     {"<< (catch <<B>>) || <<B>> <= Bs>>", binary_comp,false},
+		     {"#{K => V || {K,V} <- KVs}", map_comp,false},
+		     {"#{K => (catch V) || {K,V} <- KVs}", map_comp,false},
 		     {"#state{ a = A, b = B}", record_expr,false},
 		     {"#state{}", record_expr,false},
 		     {"#s{ a = #def{ a=A }, b = B}", record_expr,false},
@@ -464,7 +466,19 @@ test_maybe_expr_ann(Config) when is_list(Config) ->
     [MaybeMatchAnn1, MaybeMatchAnn2, MatchAnn1] = erl_syntax:maybe_expr_body(MaybeNoElseAnn),
     NoElseAnn = erl_syntax:maybe_expr_else(MaybeNoElseAnn),
     [] = erl_syntax:get_ann(NoElseAnn),
+    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_files(Config) ->
-- 
2.51.0

openSUSE Build Service is sponsored by