@@ -276,6 +276,20 @@ module MapperUtils = struct
276276 renamed @ template_args_to_insert
277277end
278278
279+ module TypeReplace = struct
280+ let ext_replace_type = " replace.type"
281+
282+ (* Extract a core_type payload from an expression extension of the form
283+ %replace.type(: <core_type>) *)
284+ let core_type_of_expr_extension (expr : Parsetree.expression ) =
285+ match expr.pexp_desc with
286+ | Pexp_extension ({txt} , payload ) when txt = ext_replace_type -> (
287+ match payload with
288+ | PTyp ct -> Some ct
289+ | _ -> None )
290+ | _ -> None
291+ end
292+
279293type args_ctx = {
280294 labelled : (string , Parsetree .expression ) Hashtbl .t ;
281295 unlabelled : (int , Parsetree .expression ) Hashtbl .t ;
@@ -552,6 +566,55 @@ let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) =
552566 {
553567 Ast_mapper. default_mapper with
554568 extension = remap_needed_extensions;
569+ (* Replace deprecated type references when a %replace.type(: ...) template
570+ is provided. *)
571+ typ =
572+ (fun mapper (ct : Parsetree.core_type ) ->
573+ match ct.ptyp_desc with
574+ | Ptyp_constr ({loc} , args ) -> (
575+ (* Build a lookup of deprecated type references (by source loc) ->
576+ core_type template. *)
577+ let loc_contains (a : Location.t ) (b : Location.t ) =
578+ let a_start = a.Location. loc_start.pos_cnum in
579+ let a_end = a.Location. loc_end.pos_cnum in
580+ let b_start = b.Location. loc_start.pos_cnum in
581+ let b_end = b.Location. loc_end.pos_cnum in
582+ a_start < = b_start && a_end > = b_end
583+ in
584+ let replace_template_opt =
585+ (* We expect the cmt to have recorded deprecations for type
586+ references without a specific context; we also only consider
587+ entries whose migration_template is a %replace.type(: ...). *)
588+ deprecated_used
589+ |> List. find_map (fun (d : Cmt_utils.deprecated_used ) ->
590+ match d.migration_template with
591+ | Some e -> (
592+ match TypeReplace. core_type_of_expr_extension e with
593+ | Some ct
594+ when loc_contains loc d.source_loc
595+ || loc_contains d.source_loc loc ->
596+ Some ct
597+ | _ -> None )
598+ | None -> None )
599+ in
600+ match replace_template_opt with
601+ | Some template_ct -> (
602+ (* Transfer all source type arguments as-is. *)
603+ let mapped_args = List. map (mapper.Ast_mapper. typ mapper) args in
604+ match template_ct.ptyp_desc with
605+ | Ptyp_constr (new_lid , templ_args ) ->
606+ let new_args = templ_args @ mapped_args in
607+ let ct' =
608+ {ct with ptyp_desc = Ptyp_constr (new_lid, new_args)}
609+ in
610+ mapper.Ast_mapper. typ mapper ct'
611+ | _ ->
612+ (* If the template isn't a constructor, fall back to the
613+ template itself and drop the original args. *)
614+ let ct' = {template_ct with ptyp_loc = ct.ptyp_loc} in
615+ mapper.Ast_mapper. typ mapper ct')
616+ | None -> Ast_mapper. default_mapper.typ mapper ct)
617+ | _ -> Ast_mapper. default_mapper.typ mapper ct);
555618 expr =
556619 (fun mapper exp ->
557620 match exp with
0 commit comments