Skip to content

Commit f04c23d

Browse files
committed
support replacing types
1 parent 2d32f99 commit f04c23d

File tree

5 files changed

+75
-4
lines changed

5 files changed

+75
-4
lines changed

packages/@rescript/runtime/Js_dict.res

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
/*** Provides a simple key-value dictionary abstraction over native JavaScript objects */
2626

2727
/** The dict type */
28+
@deprecated({
29+
reason: "Use `dict` directly instead.",
30+
migrate: %replace.type(: dict),
31+
})
2832
type t<'a> = dict<'a>
2933

3034
/** The key type, an alias of string */

packages/@rescript/runtime/Js_dict.resi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ Dictionary type (ie an '{ }' JS object). However it is restricted to hold a
3939
single type; therefore values must have the same type. This Dictionary type is
4040
mostly used with the Js_json.t type.
4141
*/
42+
@deprecated({
43+
reason: "Use `dict` directly instead.",
44+
migrate: %replace.type(: dict),
45+
})
4246
type t<'a> = dict<'a>
4347

4448
/**

tests/tools_tests/src/expected/StdlibMigration_Dict.res.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ let values2 = Dict.valuesToArray(d)
1818
let entries1 = d->Dict.toArray
1919
let entries2 = Dict.toArray(d)
2020

21-
let dStr: Js.Dict.t<string> = Dict.make()
21+
let dStr: dict<string> = Dict.make()
2222
let del1 = dStr->Dict.delete("k")
2323
let del2 = Dict.delete(dStr, "k")
2424

25-
let empty1: Js.Dict.t<int> = Dict.make()
25+
let empty1: dict<int> = Dict.make()
2626

2727
let fromArray1 = [("a", 1), ("b", 2)]->Dict.fromArray
2828
let fromArray2 = Dict.fromArray([("a", 1), ("b", 2)])

tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Dict.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ let values2 = Dict.valuesToArray(d)
2020
let entries1 = d->Dict.toArray
2121
let entries2 = Dict.toArray(d)
2222

23-
let dStr: Js.Dict.t<string> = Dict.make()
23+
let dStr: dict<string> = Dict.make()
2424
let del1 = dStr->Dict.delete("k")
2525
let del2 = Dict.delete(dStr, "k")
2626

27-
let empty1: Js.Dict.t<int> = Dict.make()
27+
let empty1: dict<int> = Dict.make()
2828

2929
let fromArray1 = [("a", 1), ("b", 2)]->Dict.fromArray
3030
let fromArray2 = Dict.fromArray([("a", 1), ("b", 2)])

tools/src/migrate.ml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,20 @@ module MapperUtils = struct
276276
renamed @ template_args_to_insert
277277
end
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+
279293
type 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

Comments
 (0)