@@ -253,45 +253,8 @@ expand({'try', Meta, [Opts]}, S, E) ->
253253
254254% % Comprehensions
255255
256- expand ({for , Meta , [_ | _ ] = Args }, S , E ) ->
257- assert_no_match_or_guard_scope (Meta , " for" , S , E ),
258-
259- {Cases , Block } =
260- case elixir_utils :split_last (Args ) of
261- {OuterCases , OuterOpts } when is_list (OuterOpts ) ->
262- case elixir_utils :split_last (OuterCases ) of
263- {InnerCases , InnerOpts } when is_list (InnerOpts ) ->
264- {InnerCases , InnerOpts ++ OuterOpts };
265- _ ->
266- {OuterCases , OuterOpts }
267- end ;
268- _ ->
269- {Args , []}
270- end ,
271-
272- validate_opts (Meta , for , [do , into , uniq , reduce ], Block , E ),
273-
274- {Expr , Opts } =
275- case lists :keytake (do , 1 , Block ) of
276- {value , {do , Do }, DoOpts } ->
277- {Do , DoOpts };
278- false ->
279- form_error (Meta , E , ? MODULE , {missing_option , for , [do ]})
280- end ,
281-
282- {EOpts , SO , EO } = expand (Opts , elixir_env :reset_unused_vars (S ), E ),
283- {ECases , SC , EC } = mapfold (fun expand_for /3 , SO , EO , Cases ),
284- assert_generator_start (Meta , ECases , E ),
285-
286- {EExpr , SE , EE } =
287- case validate_for_options (EOpts , false , false , false ) of
288- {ok , MaybeReduce } -> expand_for_do_block (Meta , Expr , SC , EC , MaybeReduce );
289- {error , Error } -> form_error (Meta , E , ? MODULE , Error )
290- end ,
291-
292- {{for , Meta , ECases ++ [[{do , EExpr } | EOpts ]]},
293- elixir_env :merge_and_check_unused_vars (SE , S , EE ),
294- E };
256+ expand ({for , _ , [_ | _ ] } = Expr , S , E ) ->
257+ expand_for (Expr , S , E , true );
295258
296259% % With
297260
@@ -563,6 +526,12 @@ expand_block([], Acc, _Meta, S, E) ->
563526expand_block ([H ], Acc , Meta , S , E ) ->
564527 {EH , SE , EE } = expand (H , S , E ),
565528 expand_block ([], [EH | Acc ], Meta , SE , EE );
529+ expand_block ([{for , _ , [_ | _ ]} = H | T ], Acc , Meta , S , E ) ->
530+ {EH , SE , EE } = expand_for (H , S , E , false ),
531+ expand_block (T , [EH | Acc ], Meta , SE , EE );
532+ expand_block ([{'=' , _ , [{'_' , _ , Ctx }, {for , _ , [_ | _ ]} = H ]} | T ], Acc , Meta , S , E ) when is_atom (Ctx ) ->
533+ {EH , SE , EE } = expand_for (H , S , E , false ),
534+ expand_block (T , [EH | Acc ], Meta , SE , EE );
566535expand_block ([H | T ], Acc , Meta , S , E ) ->
567536 {EH , SE , EE } = expand (H , S , E ),
568537
@@ -741,18 +710,65 @@ generated_case_clauses([{do, Clauses}]) ->
741710
742711% % Comprehensions
743712
744- validate_for_options ([{into , _ } = Pair | Opts ], _Into , Uniq , Reduce ) ->
745- validate_for_options (Opts , Pair , Uniq , Reduce );
746- validate_for_options ([{uniq , Boolean } = Pair | Opts ], Into , _Uniq , Reduce ) when is_boolean (Boolean ) ->
747- validate_for_options (Opts , Into , Pair , Reduce );
748- validate_for_options ([{uniq , Value } | _ ], _ , _ , _ ) ->
713+ expand_for ({for , Meta , [_ | _ ] = Args }, S , E , Return ) ->
714+ assert_no_match_or_guard_scope (Meta , " for" , S , E ),
715+
716+ {Cases , Block } =
717+ case elixir_utils :split_last (Args ) of
718+ {OuterCases , OuterOpts } when is_list (OuterOpts ) ->
719+ case elixir_utils :split_last (OuterCases ) of
720+ {InnerCases , InnerOpts } when is_list (InnerOpts ) ->
721+ {InnerCases , InnerOpts ++ OuterOpts };
722+ _ ->
723+ {OuterCases , OuterOpts }
724+ end ;
725+ _ ->
726+ {Args , []}
727+ end ,
728+
729+ validate_opts (Meta , for , [do , into , uniq , reduce ], Block , E ),
730+
731+ {Expr , Opts } =
732+ case lists :keytake (do , 1 , Block ) of
733+ {value , {do , Do }, DoOpts } ->
734+ {Do , DoOpts };
735+ false ->
736+ form_error (Meta , E , ? MODULE , {missing_option , for , [do ]})
737+ end ,
738+
739+ {EOpts , SO , EO } = expand (Opts , elixir_env :reset_unused_vars (S ), E ),
740+ {ECases , SC , EC } = mapfold (fun expand_for_generator /3 , SO , EO , Cases ),
741+ assert_generator_start (Meta , ECases , E ),
742+
743+ {{EExpr , SE , EE }, NormalizedOpts } =
744+ case validate_for_options (EOpts , false , false , false , Return , Meta , E , []) of
745+ {ok , MaybeReduce , NOpts } -> {expand_for_do_block (Meta , Expr , SC , EC , MaybeReduce ), NOpts };
746+ {error , Error } -> {form_error (Meta , E , ? MODULE , Error ), EOpts }
747+ end ,
748+
749+ {{for , Meta , ECases ++ [[{do , EExpr } | NormalizedOpts ]]},
750+ elixir_env :merge_and_check_unused_vars (SE , S , EE ),
751+ E }.
752+
753+ validate_for_options ([{into , _ } = Pair | Opts ], _Into , Uniq , Reduce , Return , Meta , E , Acc ) ->
754+ validate_for_options (Opts , Pair , Uniq , Reduce , Return , Meta , E , [Pair | Acc ]);
755+ validate_for_options ([{uniq , Boolean } = Pair | Opts ], Into , _Uniq , Reduce , Return , Meta , E , Acc ) when is_boolean (Boolean ) ->
756+ validate_for_options (Opts , Into , Pair , Reduce , Return , Meta , E , [Pair | Acc ]);
757+ validate_for_options ([{uniq , Value } | _ ], _ , _ , _ , _ , _ , _ , _ ) ->
749758 {error , {for_invalid_uniq , Value }};
750- validate_for_options ([{reduce , _ } = Pair | Opts ], Into , Uniq , _Reduce ) ->
751- validate_for_options (Opts , Into , Uniq , Pair );
752- validate_for_options ([], Into , Uniq , {reduce , _ }) when Into /= false ; Uniq /= false ->
759+ validate_for_options ([{reduce , _ } = Pair | Opts ], Into , Uniq , _Reduce , Return , Meta , E , Acc ) ->
760+ validate_for_options (Opts , Into , Uniq , Pair , Return , Meta , E , [ Pair | Acc ] );
761+ validate_for_options ([], Into , Uniq , {reduce , _ }, _Return , _Meta , _E , _Acc ) when Into /= false ; Uniq /= false ->
753762 {error , for_conflicting_reduce_into_uniq };
754- validate_for_options ([], _Into , _Uniq , Reduce ) ->
755- {ok , Reduce }.
763+ validate_for_options ([], _Into = false , Uniq , Reduce = false , Return = true , Meta , E , Acc ) ->
764+ Pair = {into , []},
765+ validate_for_options ([Pair ], Pair , Uniq , Reduce , Return , Meta , E , Acc );
766+ validate_for_options ([], Into = false , {uniq , true }, Reduce = false , Return = false , Meta , E , Acc ) ->
767+ elixir_errors :form_warn (Meta , E , ? MODULE , for_with_unused_uniq ),
768+ AccWithoutUniq = lists :keydelete (uniq , 1 , Acc ),
769+ validate_for_options ([], Into , false , Reduce , Return , Meta , E , AccWithoutUniq );
770+ validate_for_options ([], _Into , _Uniq , Reduce , _Return , _Meta , _E , Acc ) ->
771+ {ok , Reduce , lists :reverse (Acc )}.
756772
757773expand_for_do_block (Meta , [{'->' , _ , _ } | _ ], _S , E , false ) ->
758774 form_error (Meta , E , ? MODULE , for_without_reduce_bad_block );
@@ -1031,12 +1047,12 @@ expand_aliases({'__aliases__', Meta, _} = Alias, S, E, Report) ->
10311047
10321048% % Comprehensions
10331049
1034- expand_for ({'<-' , Meta , [Left , Right ]}, S , E ) ->
1050+ expand_for_generator ({'<-' , Meta , [Left , Right ]}, S , E ) ->
10351051 {ERight , SR , ER } = expand (Right , S , E ),
10361052 SM = elixir_env :reset_read (SR , S ),
10371053 {[ELeft ], SL , EL } = elixir_clauses :head ([Left ], SM , ER ),
10381054 {{'<-' , Meta , [ELeft , ERight ]}, SL , EL };
1039- expand_for ({'<<>>' , Meta , Args } = X , S , E ) when is_list (Args ) ->
1055+ expand_for_generator ({'<<>>' , Meta , Args } = X , S , E ) when is_list (Args ) ->
10401056 case elixir_utils :split_last (Args ) of
10411057 {LeftStart , {'<-' , OpMeta , [LeftEnd , Right ]}} ->
10421058 {ERight , SR , ER } = expand (Right , S , E ),
@@ -1048,7 +1064,7 @@ expand_for({'<<>>', Meta, Args} = X, S, E) when is_list(Args) ->
10481064 _ ->
10491065 expand (X , S , E )
10501066 end ;
1051- expand_for (X , S , E ) ->
1067+ expand_for_generator (X , S , E ) ->
10521068 expand (X , S , E ).
10531069
10541070assert_generator_start (_ , [{'<-' , _ , [_ , _ ]} | _ ], _ ) ->
@@ -1170,6 +1186,8 @@ format_error(for_without_reduce_bad_block) ->
11701186 " the do block was written using acc -> expr clauses but the :reduce option was not given" ;
11711187format_error (for_generator_start ) ->
11721188 " for comprehensions must start with a generator" ;
1189+ format_error (for_with_unused_uniq ) ->
1190+ " the :uniq option has no effect since the result of the for comprehension is not used" ;
11731191format_error (unhandled_arrow_op ) ->
11741192 " unhandled operator ->" ;
11751193format_error (as_in_multi_alias_call ) ->
0 commit comments