@@ -64,6 +64,66 @@ module ExprUtils = struct
6464end
6565
6666module MapperUtils = struct
67+ (* Collect placeholder usages anywhere inside an expression. *)
68+ let collect_placeholders (expr : Parsetree.expression ) =
69+ let labelled = ref StringSet. empty in
70+ let unlabelled = ref IntSet. empty in
71+ let open Ast_iterator in
72+ let iter =
73+ {
74+ default_iterator with
75+ expr =
76+ (fun self e ->
77+ (match InsertExt. placeholder_of_expr e with
78+ | Some (InsertExt. Labelled name ) ->
79+ labelled := StringSet. add name ! labelled
80+ | Some (InsertExt. Unlabelled i ) when i > = 0 ->
81+ unlabelled := IntSet. add i ! unlabelled
82+ | _ -> () );
83+ default_iterator.expr self e);
84+ }
85+ in
86+ iter.expr iter expr;
87+ (! labelled, ! unlabelled)
88+
89+ (* Replace placeholders anywhere inside an expression using the given
90+ source arguments. *)
91+ let replace_placeholders_in_expr (expr : Parsetree.expression )
92+ (source_args : (Asttypes.arg_label * Parsetree.expression) list ) =
93+ let labelled = Hashtbl. create 8 in
94+ let unlabelled = Hashtbl. create 8 in
95+ let idx = ref 0 in
96+ source_args
97+ |> List. iter (fun (lbl , arg ) ->
98+ match lbl with
99+ | Asttypes. Nolabel ->
100+ Hashtbl. replace unlabelled ! idx arg;
101+ incr idx
102+ | Asttypes. Labelled {txt} | Optional {txt} ->
103+ Hashtbl. replace labelled txt arg);
104+ let find = function
105+ | `Labelled name -> Hashtbl. find_opt labelled name
106+ | `Unlabelled i -> Hashtbl. find_opt unlabelled i
107+ in
108+ let mapper =
109+ {
110+ Ast_mapper. default_mapper with
111+ expr =
112+ (fun mapper exp ->
113+ match InsertExt. placeholder_of_expr exp with
114+ | Some (InsertExt. Labelled name ) -> (
115+ match find (`Labelled name) with
116+ | Some arg -> arg
117+ | None -> exp)
118+ | Some (InsertExt. Unlabelled i ) -> (
119+ match find (`Unlabelled i) with
120+ | Some arg -> arg
121+ | None -> exp)
122+ | None -> Ast_mapper. default_mapper.expr mapper exp);
123+ }
124+ in
125+ mapper.expr mapper expr
126+
67127 let build_labelled_args_map template_args =
68128 template_args
69129 |> List. filter_map (fun (label , arg ) ->
@@ -89,23 +149,6 @@ module MapperUtils = struct
89149 - unlabelled_positions_to_insert: 0-based indices of unlabelled source args to drop
90150 *)
91151 let get_template_args_to_insert mapper template_args source_args =
92- let find_source_labelled name =
93- source_args
94- |> List. find_map (fun (label , arg ) ->
95- match label with
96- | Asttypes. Labelled {txt = l } | Optional {txt = l } ->
97- if l = name then Some arg else None
98- | _ -> None )
99- in
100- let find_source_unlabelled target =
101- let rec loop i = function
102- | [] -> None
103- | (Asttypes. Nolabel, arg ) :: rest ->
104- if i = target then Some arg else loop (i + 1 ) rest
105- | _ :: rest -> loop i rest
106- in
107- loop 0 source_args
108- in
109152 let is_unit_expr (e : Parsetree.expression ) =
110153 match e.pexp_desc with
111154 | Pexp_construct ({txt = Lident "()" } , None) -> true
@@ -121,31 +164,17 @@ module MapperUtils = struct
121164 - used_unlabelled: 0-based positions of unlabelled args consumed. *)
122165 let accumulate_template_arg (rev_args , used_labelled , used_unlabelled )
123166 (label , arg ) =
124- match InsertExt. placeholder_of_expr arg with
125- | Some (InsertExt. Labelled name ) -> (
126- match label with
127- | Asttypes. Nolabel -> (
128- match find_source_labelled name with
129- | Some arg' ->
130- ( (Asttypes. Nolabel , arg') :: rev_args,
131- StringSet. add name used_labelled,
132- used_unlabelled )
133- | None -> (rev_args, used_labelled, used_unlabelled))
134- | _ -> (rev_args, used_labelled, used_unlabelled))
135- | Some (InsertExt. Unlabelled target ) when target > = 0 -> (
136- match find_source_unlabelled target with
137- | Some arg' ->
138- ( (label, arg') :: rev_args,
139- used_labelled,
140- IntSet. add target used_unlabelled )
141- | None -> (rev_args, used_labelled, used_unlabelled))
142- | Some _ -> (rev_args, used_labelled, used_unlabelled)
143- | None ->
144- if is_unit_expr arg then (rev_args, used_labelled, used_unlabelled)
145- else
146- ( (label, mapper.Ast_mapper. expr mapper arg) :: rev_args,
147- used_labelled,
148- used_unlabelled )
167+ (* Always perform nested replacement inside the argument expression,
168+ and collect which placeholders were used so we can drop them from the
169+ original call's arguments. *)
170+ let labelled_used_here, unlabelled_used_here = collect_placeholders arg in
171+ let arg_replaced = replace_placeholders_in_expr arg source_args in
172+ if is_unit_expr arg_replaced then
173+ (rev_args, used_labelled, used_unlabelled)
174+ else
175+ ( (label, mapper.Ast_mapper. expr mapper arg_replaced) :: rev_args,
176+ StringSet. union used_labelled labelled_used_here,
177+ IntSet. union used_unlabelled unlabelled_used_here )
149178 in
150179 let rev_args, labelled_set, unlabelled_set =
151180 List. fold_left accumulate_template_arg
0 commit comments