From 9e37797069d3d9b0d1a48f21d310f8b744856fbe Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 9 Jul 2025 13:23:11 +0200 Subject: [PATCH 01/43] wip migrate command --- analysis/src/Cmt.ml | 14 ++ compiler/ml/builtin_attributes.ml | 40 ++++- compiler/ml/cmt_format.ml | 11 +- compiler/ml/cmt_format.mli | 4 + compiler/ml/cmt_utils.ml | 11 ++ .../src/expected/DeprecatedStuff.res.expected | 1 + .../src/expected/FileToMigrate.res.expected | 6 + .../src/migrate/DeprecatedStuff.res | 9 + .../tools_tests/src/migrate/FileToMigrate.res | 9 + tests/tools_tests/test.sh | 10 ++ tools/bin/main.ml | 10 ++ tools/src/tools.ml | 161 ++++++++++++++++++ 12 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 compiler/ml/cmt_utils.ml create mode 100644 tests/tools_tests/src/expected/DeprecatedStuff.res.expected create mode 100644 tests/tools_tests/src/expected/FileToMigrate.res.expected create mode 100644 tests/tools_tests/src/migrate/DeprecatedStuff.res create mode 100644 tests/tools_tests/src/migrate/FileToMigrate.res diff --git a/analysis/src/Cmt.ml b/analysis/src/Cmt.ml index a433d12908..ac1d5ae595 100644 --- a/analysis/src/Cmt.ml +++ b/analysis/src/Cmt.ml @@ -51,3 +51,17 @@ let fullsFromModule ~package ~moduleName = let loadFullCmtFromPath ~path = let uri = Uri.fromPath path in fullFromUri ~uri + +let loadCmtInfosFromPath ~path = + let uri = Uri.fromPath path in + match Packages.getPackage ~uri with + | None -> None + | Some package -> ( + let moduleName = + BuildSystem.namespacedName package.namespace (FindFiles.getName path) + in + match Hashtbl.find_opt package.pathsForModule moduleName with + | Some paths -> + let cmt = getCmtPath ~uri paths in + Shared.tryReadCmt cmt + | None -> None) diff --git a/compiler/ml/builtin_attributes.ml b/compiler/ml/builtin_attributes.ml index 5d110eda75..846e8e77cb 100644 --- a/compiler/ml/builtin_attributes.ml +++ b/compiler/ml/builtin_attributes.ml @@ -79,10 +79,46 @@ let rec deprecated_of_attrs = function Some (string_of_opt_payload p) | _ :: tl -> deprecated_of_attrs tl +let rec deprecated_of_attrs_with_migrate = function + | [] -> None + | ( {txt = "deprecated"; _}, + PStr [{pstr_desc = Pstr_eval ({pexp_desc = Pexp_record (fields, _)}, _)}] + ) + :: _ -> ( + let reason = + fields + |> List.find_map (fun field -> + match field with + | { + lid = {txt = Lident "reason"}; + x = {pexp_desc = Pexp_constant (Pconst_string (reason, _))}; + } -> + Some reason + | _ -> None) + in + let migration_template = + fields + |> List.find_map (fun field -> + match field with + | {lid = {txt = Lident "migrate"}; x = migration_template} -> + Some migration_template + | _ -> None) + in + + (* TODO: Validate and error if expected shape mismatches *) + match reason with + | Some reason -> Some (reason, migration_template) + | None -> None) + | ({txt = "ocaml.deprecated" | "deprecated"; _}, p) :: _ -> + Some (string_of_opt_payload p, None) + | _ :: tl -> deprecated_of_attrs_with_migrate tl + let check_deprecated loc attrs s = - match deprecated_of_attrs attrs with + match deprecated_of_attrs_with_migrate attrs with | None -> () - | Some txt -> Location.deprecated loc (cat s txt) + | Some (txt, migration_template) -> + !Cmt_utils.record_deprecated_used loc txt migration_template; + Location.deprecated loc (cat s txt) let check_deprecated_inclusion ~def ~use loc attrs1 attrs2 s = match (deprecated_of_attrs attrs1, deprecated_of_attrs attrs2) with diff --git a/compiler/ml/cmt_format.ml b/compiler/ml/cmt_format.ml index 907f2e7122..cb566738ad 100644 --- a/compiler/ml/cmt_format.ml +++ b/compiler/ml/cmt_format.ml @@ -63,6 +63,7 @@ type cmt_infos = { cmt_imports : (string * Digest.t option) list; cmt_interface_digest : Digest.t option; cmt_use_summaries : bool; + cmt_extra_info: Cmt_utils.cmt_extra_info; } type error = @@ -154,15 +155,22 @@ let read_cmi filename = let saved_types = ref [] let value_deps = ref [] +let deprecated_used = ref [] let clear () = saved_types := []; - value_deps := [] + value_deps := []; + deprecated_used := [] let add_saved_type b = saved_types := b :: !saved_types let get_saved_types () = !saved_types let set_saved_types l = saved_types := l +let record_deprecated_used source_loc deprecated_text migration_template = + deprecated_used := {Cmt_utils.source_loc; deprecated_text; migration_template} :: !deprecated_used + +let _ = Cmt_utils.record_deprecated_used := record_deprecated_used + let record_value_dependency vd1 vd2 = if vd1.Types.val_loc <> vd2.Types.val_loc then value_deps := (vd1, vd2) :: !value_deps @@ -197,6 +205,7 @@ let save_cmt filename modname binary_annots sourcefile initial_env cmi = cmt_imports = List.sort compare (Env.imports ()); cmt_interface_digest = this_crc; cmt_use_summaries = need_to_clear_env; + cmt_extra_info = {deprecated_used = !deprecated_used}; } in output_cmt oc cmt) end; diff --git a/compiler/ml/cmt_format.mli b/compiler/ml/cmt_format.mli index 1a84aa68d0..d59a5053b5 100644 --- a/compiler/ml/cmt_format.mli +++ b/compiler/ml/cmt_format.mli @@ -63,6 +63,7 @@ type cmt_infos = { cmt_imports: (string * Digest.t option) list; cmt_interface_digest: Digest.t option; cmt_use_summaries: bool; + cmt_extra_info: Cmt_utils.cmt_extra_info; } type error = Not_a_typedtree of string @@ -111,6 +112,9 @@ val set_saved_types : binary_part list -> unit val record_value_dependency : Types.value_description -> Types.value_description -> unit +val record_deprecated_used : + Location.t -> string -> Parsetree.expression option -> unit + (* val is_magic_number : string -> bool diff --git a/compiler/ml/cmt_utils.ml b/compiler/ml/cmt_utils.ml new file mode 100644 index 0000000000..fd73347a05 --- /dev/null +++ b/compiler/ml/cmt_utils.ml @@ -0,0 +1,11 @@ +type deprecated_used = { + source_loc: Location.t; + deprecated_text: string; + migration_template: Parsetree.expression option; +} + +type cmt_extra_info = {deprecated_used: deprecated_used list} + +let record_deprecated_used : + (Location.t -> string -> Parsetree.expression option -> unit) ref = + ref (fun _ _ _ -> ()) diff --git a/tests/tools_tests/src/expected/DeprecatedStuff.res.expected b/tests/tools_tests/src/expected/DeprecatedStuff.res.expected new file mode 100644 index 0000000000..4e72aff940 --- /dev/null +++ b/tests/tools_tests/src/expected/DeprecatedStuff.res.expected @@ -0,0 +1 @@ +DeprecatedStuff.res: File did not need migration diff --git a/tests/tools_tests/src/expected/FileToMigrate.res.expected b/tests/tools_tests/src/expected/FileToMigrate.res.expected new file mode 100644 index 0000000000..d1a1b3ceab --- /dev/null +++ b/tests/tools_tests/src/expected/FileToMigrate.res.expected @@ -0,0 +1,6 @@ +let someNiceString = String.slice("abcdefg", ~start=2, ~end=5) + +let someNiceString2 = String.slice(String.slice("abcdefg", ~start=0, ~end=1), ~start=2, ~end=5) + +let someNiceString3 = "abcdefg"->String.slice(~start=2, ~end=5) + diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.res b/tests/tools_tests/src/migrate/DeprecatedStuff.res new file mode 100644 index 0000000000..de4f39fe0b --- /dev/null +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.res @@ -0,0 +1,9 @@ +@deprecated({ + reason: "Use `String.slice` instead", + migrate: String.slice( + ~start=%insert.labelledArgument("from"), + ~end=%insert.labelledArgument("to_"), + ), +}) +@send +external slice: (string, ~from: int, ~to_: int) => string = "slice" diff --git a/tests/tools_tests/src/migrate/FileToMigrate.res b/tests/tools_tests/src/migrate/FileToMigrate.res new file mode 100644 index 0000000000..dc241cc5ee --- /dev/null +++ b/tests/tools_tests/src/migrate/FileToMigrate.res @@ -0,0 +1,9 @@ +let someNiceString = DeprecatedStuff.slice("abcdefg", ~from=2, ~to_=5) + +let someNiceString2 = DeprecatedStuff.slice( + DeprecatedStuff.slice("abcdefg", ~from=0, ~to_=1), + ~from=2, + ~to_=5, +) + +let someNiceString3 = "abcdefg"->DeprecatedStuff.slice(~from=2, ~to_=5) diff --git a/tests/tools_tests/test.sh b/tests/tools_tests/test.sh index 73e42acfd6..2d69e9e9fa 100755 --- a/tests/tools_tests/test.sh +++ b/tests/tools_tests/test.sh @@ -33,6 +33,16 @@ for file in src/docstrings-format/*.{res,resi,md}; do fi done +# Test migrate command +for file in src/migrate/*.{res,resi}; do + output="src/expected/$(basename $file).expected" + ../../_build/install/default/bin/rescript-tools migrate "$file" --stdout > $output + # # CI. We use LF, and the CI OCaml fork prints CRLF. Convert. + if [ "$RUNNER_OS" == "Windows" ]; then + perl -pi -e 's/\r\n/\n/g' -- $output + fi +done + warningYellow='\033[0;33m' successGreen='\033[0;32m' reset='\033[0m' diff --git a/tools/bin/main.ml b/tools/bin/main.ml index 88f7ffa575..6f1a76ff45 100644 --- a/tools/bin/main.ml +++ b/tools/bin/main.ml @@ -32,6 +32,7 @@ Usage: rescript-tools [command] Commands: +migrate [--stdout] Runs the migration tool on the given file doc Generate documentation format-codeblocks Format ReScript code blocks [--stdout] Output to stdout @@ -66,6 +67,15 @@ let main () = in logAndExit (Tools.extractDocs ~entryPointFile:path ~debug:false) | _ -> logAndExit (Error docHelp)) + | "migrate" :: file :: opts -> ( + let isStdout = List.mem "--stdout" opts in + let outputMode = if isStdout then `Stdout else `File in + match + (Tools.Migrate.migrate ~entryPointFile:file ~outputMode, outputMode) + with + | Ok content, `Stdout -> print_endline content + | result, `File -> logAndExit result + | Error e, _ -> logAndExit (Error e)) | "format-codeblocks" :: rest -> ( match rest with | ["-h"] | ["--help"] -> logAndExit (Ok formatCodeblocksHelp) diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 1722fdda07..b7e51dc192 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1292,3 +1292,164 @@ module ExtractCodeblocks = struct ]) |> Protocol.array) end + +module StringMap = Map.Make (String) + +module Migrate = struct + let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = + let loc_to_deprecated_use = Hashtbl.create (List.length deprecated_used) in + deprecated_used + |> List.iter (fun ({Cmt_utils.source_loc} as d) -> + Hashtbl.replace loc_to_deprecated_use source_loc d); + let mapper = + { + Ast_mapper.default_mapper with + expr = + (fun mapper exp -> + match exp with + | { + pexp_desc = + Pexp_apply {funct = {pexp_loc = fn_loc}; args = source_args}; + } + when Hashtbl.mem loc_to_deprecated_use fn_loc -> ( + let deprecated_info = Hashtbl.find loc_to_deprecated_use fn_loc in + Hashtbl.remove loc_to_deprecated_use fn_loc; + + let source_args = + source_args + |> List.map (fun (label, arg) -> + (label, mapper.expr mapper arg)) + in + + (* TODO: Here we could add strict and partial mode, to control if args are merged or not. *) + match deprecated_info.migration_template with + | Some + { + pexp_desc = + Pexp_apply + { + funct = template_funct; + args = template_args; + partial; + transformed_jsx; + }; + } -> + let labelled_args_map = + template_args + |> List.filter_map (fun (label, arg) -> + match (label, arg) with + | ( ( Asttypes.Labelled {txt = label} + | Optional {txt = label} ), + { + Parsetree.pexp_desc = + Pexp_extension + ( {txt = "insert.labelledArgument"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_constant + (Pconst_string + (arg_name, _)); + }, + _ ); + }; + ] ); + } ) -> + Some (arg_name, label) + | _ -> None) + |> StringMap.of_list + in + { + exp with + pexp_desc = + Pexp_apply + { + funct = template_funct; + args = + source_args + |> List.map (fun (label, arg) -> + match label with + | Asttypes.Labelled {loc; txt = label} + | Asttypes.Optional {loc; txt = label} + when StringMap.mem label labelled_args_map -> + let mapped_label_name = + StringMap.find label labelled_args_map + in + ( Asttypes.Labelled + {loc; txt = mapped_label_name}, + arg ) + | label -> (label, arg)); + partial; + transformed_jsx; + }; + } + | _ -> + (* TODO: More elaborate warnings etc *) + (* Invalid config. *) + exp) + | _ -> Ast_mapper.default_mapper.expr mapper exp); + } + in + mapper + + let migrate ~entryPointFile ~outputMode = + let path = + match Filename.is_relative entryPointFile with + | true -> Unix.realpath entryPointFile + | false -> entryPointFile + in + let result = + if Filename.check_suffix path ".res" then + let parser = + Res_driver.parsing_engine.parse_implementation ~for_printer:true + in + let {Res_driver.parsetree; comments; source} = parser ~filename:path in + match Cmt.loadCmtInfosFromPath ~path with + | None -> + Error + (Printf.sprintf + "error: failed to run migration for %s because build artifacts \ + could not be found. try to build the project" + path) + | Some {cmt_extra_info = {deprecated_used}} -> + let mapper = makeMapper deprecated_used in + let astMapped = mapper.structure mapper parsetree in + Ok + ( Res_printer.print_implementation + ~width:Res_printer.default_print_width astMapped ~comments, + source ) + else if Filename.check_suffix path ".resi" then + let parser = + Res_driver.parsing_engine.parse_interface ~for_printer:true + in + let {Res_driver.parsetree = signature; comments; source} = + parser ~filename:path + in + let mapper = makeMapper [] in + let astMapped = mapper.signature mapper signature in + Ok + ( Res_printer.print_interface ~width:Res_printer.default_print_width + astMapped ~comments, + source ) + else + Error + (Printf.sprintf + "File extension not supported. This command accepts .res, .resi \ + files") + in + match result with + | Error e -> Error e + | Ok (contents, source) when contents <> source -> ( + match outputMode with + | `Stdout -> Ok contents + | `File -> + let oc = open_out path in + Printf.fprintf oc "%s" contents; + close_out oc; + Ok (Filename.basename path ^ ": File migrated successfully")) + | Ok _ -> Ok (Filename.basename path ^ ": File did not need migration") +end From c9d394c9fdad28d909ecfc59298b3fc26aa8ae96 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 10 Jul 2025 10:09:05 +0200 Subject: [PATCH 02/43] fix --- .../src/expected/DeprecatedStuff.resi.expected | 1 + tests/tools_tests/src/migrate/DeprecatedStuff.res | 7 ------- tests/tools_tests/src/migrate/DeprecatedStuff.resi | 9 +++++++++ 3 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 tests/tools_tests/src/expected/DeprecatedStuff.resi.expected create mode 100644 tests/tools_tests/src/migrate/DeprecatedStuff.resi diff --git a/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected b/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected new file mode 100644 index 0000000000..7e76b1c053 --- /dev/null +++ b/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected @@ -0,0 +1 @@ +DeprecatedStuff.resi: File did not need migration diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.res b/tests/tools_tests/src/migrate/DeprecatedStuff.res index de4f39fe0b..6f0f506779 100644 --- a/tests/tools_tests/src/migrate/DeprecatedStuff.res +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.res @@ -1,9 +1,2 @@ -@deprecated({ - reason: "Use `String.slice` instead", - migrate: String.slice( - ~start=%insert.labelledArgument("from"), - ~end=%insert.labelledArgument("to_"), - ), -}) @send external slice: (string, ~from: int, ~to_: int) => string = "slice" diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.resi b/tests/tools_tests/src/migrate/DeprecatedStuff.resi new file mode 100644 index 0000000000..de4f39fe0b --- /dev/null +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.resi @@ -0,0 +1,9 @@ +@deprecated({ + reason: "Use `String.slice` instead", + migrate: String.slice( + ~start=%insert.labelledArgument("from"), + ~end=%insert.labelledArgument("to_"), + ), +}) +@send +external slice: (string, ~from: int, ~to_: int) => string = "slice" From 9b202dea60873eea710ec848bb014a8df1068eec Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 21 Jul 2025 21:34:25 +0200 Subject: [PATCH 03/43] remove comment --- tests/tools_tests/test.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tools_tests/test.sh b/tests/tools_tests/test.sh index 2d69e9e9fa..700b959b57 100755 --- a/tests/tools_tests/test.sh +++ b/tests/tools_tests/test.sh @@ -37,7 +37,6 @@ done for file in src/migrate/*.{res,resi}; do output="src/expected/$(basename $file).expected" ../../_build/install/default/bin/rescript-tools migrate "$file" --stdout > $output - # # CI. We use LF, and the CI OCaml fork prints CRLF. Convert. if [ "$RUNNER_OS" == "Windows" ]; then perl -pi -e 's/\r\n/\n/g' -- $output fi From c8b168307cd22d60b57b16f8c3a1d3abba266de4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 19:24:45 +0200 Subject: [PATCH 04/43] propagate context info for deprecated, and handle pipe without arguments --- compiler/ml/builtin_attributes.ml | 5 +- compiler/ml/builtin_attributes.mli | 7 ++- compiler/ml/cmt_format.ml | 4 +- compiler/ml/cmt_format.mli | 6 +- compiler/ml/cmt_utils.ml | 12 +++- compiler/ml/typecore.ml | 22 ++++--- compiler/ml/typetexp.ml | 5 +- compiler/ml/typetexp.mli | 6 +- .../src/expected/FileToMigrate.res.expected | 3 + .../src/migrate/DeprecatedStuff.res | 3 + .../src/migrate/DeprecatedStuff.resi | 7 +++ .../tools_tests/src/migrate/FileToMigrate.res | 3 + tools/src/tools.ml | 63 +++++++++++++++++-- 13 files changed, 121 insertions(+), 25 deletions(-) diff --git a/compiler/ml/builtin_attributes.ml b/compiler/ml/builtin_attributes.ml index 846e8e77cb..1a7d871b05 100644 --- a/compiler/ml/builtin_attributes.ml +++ b/compiler/ml/builtin_attributes.ml @@ -113,11 +113,12 @@ let rec deprecated_of_attrs_with_migrate = function Some (string_of_opt_payload p, None) | _ :: tl -> deprecated_of_attrs_with_migrate tl -let check_deprecated loc attrs s = +let check_deprecated ?deprecated_context loc attrs s = match deprecated_of_attrs_with_migrate attrs with | None -> () | Some (txt, migration_template) -> - !Cmt_utils.record_deprecated_used loc txt migration_template; + !Cmt_utils.record_deprecated_used + ?deprecated_context loc txt migration_template; Location.deprecated loc (cat s txt) let check_deprecated_inclusion ~def ~use loc attrs1 attrs2 s = diff --git a/compiler/ml/builtin_attributes.mli b/compiler/ml/builtin_attributes.mli index fd898388c7..63bf762331 100644 --- a/compiler/ml/builtin_attributes.mli +++ b/compiler/ml/builtin_attributes.mli @@ -27,7 +27,12 @@ ocaml.boxed / ocaml.unboxed *) -val check_deprecated : Location.t -> Parsetree.attributes -> string -> unit +val check_deprecated : + ?deprecated_context:Cmt_utils.deprecated_used_context -> + Location.t -> + Parsetree.attributes -> + string -> + unit val check_deprecated_inclusion : def:Location.t -> use:Location.t -> diff --git a/compiler/ml/cmt_format.ml b/compiler/ml/cmt_format.ml index cb566738ad..6d11caee27 100644 --- a/compiler/ml/cmt_format.ml +++ b/compiler/ml/cmt_format.ml @@ -166,8 +166,8 @@ let add_saved_type b = saved_types := b :: !saved_types let get_saved_types () = !saved_types let set_saved_types l = saved_types := l -let record_deprecated_used source_loc deprecated_text migration_template = - deprecated_used := {Cmt_utils.source_loc; deprecated_text; migration_template} :: !deprecated_used +let record_deprecated_used ?deprecated_context source_loc deprecated_text migration_template = + deprecated_used := {Cmt_utils.source_loc; deprecated_text; migration_template; context = deprecated_context} :: !deprecated_used let _ = Cmt_utils.record_deprecated_used := record_deprecated_used diff --git a/compiler/ml/cmt_format.mli b/compiler/ml/cmt_format.mli index d59a5053b5..8a68782996 100644 --- a/compiler/ml/cmt_format.mli +++ b/compiler/ml/cmt_format.mli @@ -113,7 +113,11 @@ val record_value_dependency : Types.value_description -> Types.value_description -> unit val record_deprecated_used : - Location.t -> string -> Parsetree.expression option -> unit + ?deprecated_context:Cmt_utils.deprecated_used_context -> + Location.t -> + string -> + Parsetree.expression option -> + unit (* diff --git a/compiler/ml/cmt_utils.ml b/compiler/ml/cmt_utils.ml index fd73347a05..c59b29b3af 100644 --- a/compiler/ml/cmt_utils.ml +++ b/compiler/ml/cmt_utils.ml @@ -1,11 +1,19 @@ +type deprecated_used_context = FunctionCall + type deprecated_used = { source_loc: Location.t; deprecated_text: string; migration_template: Parsetree.expression option; + context: deprecated_used_context option; } type cmt_extra_info = {deprecated_used: deprecated_used list} let record_deprecated_used : - (Location.t -> string -> Parsetree.expression option -> unit) ref = - ref (fun _ _ _ -> ()) + (?deprecated_context:deprecated_used_context -> + Location.t -> + string -> + Parsetree.expression option -> + unit) + ref = + ref (fun ?deprecated_context _ _ _ -> ignore deprecated_context) diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index dd2bfa7d49..9aadf5b9a8 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -2244,9 +2244,9 @@ type lazy_args = (Asttypes.Noloc.arg_label * (unit -> Typedtree.expression) option) list type targs = (Asttypes.Noloc.arg_label * Typedtree.expression option) list -let rec type_exp ~context ?recarg env sexp = +let rec type_exp ?deprecated_context ~context ?recarg env sexp = (* We now delegate everything to type_expect *) - type_expect ~context ?recarg env sexp (newvar ()) + type_expect ?deprecated_context ~context ?recarg env sexp (newvar ()) (* Typing of an expression with an expected type. This provide better error messages, and allows controlled @@ -2254,18 +2254,20 @@ let rec type_exp ~context ?recarg env sexp = In the principal case, [type_expected'] may be at generic_level. *) -and type_expect ~context ?in_function ?recarg env sexp ty_expected = +and type_expect ?deprecated_context ~context ?in_function ?recarg env sexp + ty_expected = let previous_saved_types = Cmt_format.get_saved_types () in let exp = Builtin_attributes.warning_scope sexp.pexp_attributes (fun () -> - type_expect_ ~context ?in_function ?recarg env sexp ty_expected) + type_expect_ ?deprecated_context ~context ?in_function ?recarg env sexp + ty_expected) in Cmt_format.set_saved_types (Cmt_format.Partial_expression exp :: previous_saved_types); exp -and type_expect_ ~context ?in_function ?(recarg = Rejected) env sexp ty_expected - = +and type_expect_ ?deprecated_context ~context ?in_function ?(recarg = Rejected) + env sexp ty_expected = let loc = sexp.pexp_loc in (* Record the expression type before unifying it with the expected type *) let rue exp = @@ -2282,7 +2284,9 @@ and type_expect_ ~context ?in_function ?(recarg = Rejected) env sexp ty_expected in match sexp.pexp_desc with | Pexp_ident lid -> - let path, desc = Typetexp.find_value env lid.loc lid.txt in + let path, desc = + Typetexp.find_value ?deprecated_context env lid.loc lid.txt + in (if !Clflags.annotations then let dloc = desc.Types.val_loc in let annot = @@ -2420,7 +2424,9 @@ and type_expect_ ~context ?in_function ?(recarg = Rejected) env sexp ty_expected assert (sargs <> []); begin_def (); (* one more level for non-returning functions *) - let funct = type_exp ~context:None env sfunct in + let funct = + type_exp ~deprecated_context:FunctionCall ~context:None env sfunct + in let ty = instance env funct.exp_type in end_def (); wrap_trace_gadt_instances env (lower_args env []) ty; diff --git a/compiler/ml/typetexp.ml b/compiler/ml/typetexp.ml index 942d81d663..e4de1d06eb 100644 --- a/compiler/ml/typetexp.ml +++ b/compiler/ml/typetexp.ml @@ -131,12 +131,13 @@ let find_all_constructors = let find_all_labels = find_component Env.lookup_all_labels (fun lid -> Unbound_label lid) -let find_value env loc lid = +let find_value ?deprecated_context env loc lid = Env.check_value_name (Longident.last lid) loc; let ((path, decl) as r) = find_component Env.lookup_value (fun lid -> Unbound_value lid) env loc lid in - Builtin_attributes.check_deprecated loc decl.val_attributes (Path.name path); + Builtin_attributes.check_deprecated ?deprecated_context loc + decl.val_attributes (Path.name path); r let lookup_module ?(load = false) env loc lid = diff --git a/compiler/ml/typetexp.mli b/compiler/ml/typetexp.mli index f09c26c58e..f2c1e6c191 100644 --- a/compiler/ml/typetexp.mli +++ b/compiler/ml/typetexp.mli @@ -89,7 +89,11 @@ val find_all_labels : Longident.t -> (label_description * (unit -> unit)) list val find_value : - Env.t -> Location.t -> Longident.t -> Path.t * value_description + ?deprecated_context:Cmt_utils.deprecated_used_context -> + Env.t -> + Location.t -> + Longident.t -> + Path.t * value_description val find_module : Env.t -> Location.t -> Longident.t -> Path.t * module_declaration val lookup_module : ?load:bool -> Env.t -> Location.t -> Longident.t -> Path.t diff --git a/tests/tools_tests/src/expected/FileToMigrate.res.expected b/tests/tools_tests/src/expected/FileToMigrate.res.expected index d1a1b3ceab..249e3bab54 100644 --- a/tests/tools_tests/src/expected/FileToMigrate.res.expected +++ b/tests/tools_tests/src/expected/FileToMigrate.res.expected @@ -4,3 +4,6 @@ let someNiceString2 = String.slice(String.slice("abcdefg", ~start=0, ~end=1), ~s let someNiceString3 = "abcdefg"->String.slice(~start=2, ~end=5) +let shift1 = Array.shift([1, 2, 3]) +let shift2 = [1, 2, 3]->Array.shift + diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.res b/tests/tools_tests/src/migrate/DeprecatedStuff.res index 6f0f506779..de05b9a570 100644 --- a/tests/tools_tests/src/migrate/DeprecatedStuff.res +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.res @@ -1,2 +1,5 @@ @send external slice: (string, ~from: int, ~to_: int) => string = "slice" + +@send +external shift: array<'a> => option<'a> = "shift" diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.resi b/tests/tools_tests/src/migrate/DeprecatedStuff.resi index de4f39fe0b..8240d8b62e 100644 --- a/tests/tools_tests/src/migrate/DeprecatedStuff.resi +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.resi @@ -7,3 +7,10 @@ }) @send external slice: (string, ~from: int, ~to_: int) => string = "slice" + +@send +@deprecated({ + reason: "Use `Array.shift` instead.", + migrate: Array.shift(), +}) +external shift: array<'a> => option<'a> = "shift" diff --git a/tests/tools_tests/src/migrate/FileToMigrate.res b/tests/tools_tests/src/migrate/FileToMigrate.res index dc241cc5ee..8f469f6fda 100644 --- a/tests/tools_tests/src/migrate/FileToMigrate.res +++ b/tests/tools_tests/src/migrate/FileToMigrate.res @@ -7,3 +7,6 @@ let someNiceString2 = DeprecatedStuff.slice( ) let someNiceString3 = "abcdefg"->DeprecatedStuff.slice(~from=2, ~to_=5) + +let shift1 = DeprecatedStuff.shift([1, 2, 3]) +let shift2 = [1, 2, 3]->DeprecatedStuff.shift diff --git a/tools/src/tools.ml b/tools/src/tools.ml index b7e51dc192..d0dd7ea703 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1297,10 +1297,19 @@ module StringMap = Map.Make (String) module Migrate = struct let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = - let loc_to_deprecated_use = Hashtbl.create (List.length deprecated_used) in - deprecated_used + let deprecated_function_calls = + deprecated_used + |> List.filter (fun (d : Cmt_utils.deprecated_used) -> + match d.context with + | Some FunctionCall -> true + | _ -> false) + in + let loc_to_deprecated_fn_call = + Hashtbl.create (List.length deprecated_function_calls) + in + deprecated_function_calls |> List.iter (fun ({Cmt_utils.source_loc} as d) -> - Hashtbl.replace loc_to_deprecated_use source_loc d); + Hashtbl.replace loc_to_deprecated_fn_call source_loc d); let mapper = { Ast_mapper.default_mapper with @@ -1311,9 +1320,11 @@ module Migrate = struct pexp_desc = Pexp_apply {funct = {pexp_loc = fn_loc}; args = source_args}; } - when Hashtbl.mem loc_to_deprecated_use fn_loc -> ( - let deprecated_info = Hashtbl.find loc_to_deprecated_use fn_loc in - Hashtbl.remove loc_to_deprecated_use fn_loc; + when Hashtbl.mem loc_to_deprecated_fn_call fn_loc -> ( + let deprecated_info = + Hashtbl.find loc_to_deprecated_fn_call fn_loc + in + Hashtbl.remove loc_to_deprecated_fn_call fn_loc; let source_args = source_args @@ -1391,6 +1402,46 @@ module Migrate = struct (* TODO: More elaborate warnings etc *) (* Invalid config. *) exp) + | { + pexp_desc = + Pexp_apply + { + funct = {pexp_desc = Pexp_ident {txt = Lident "->"}} as funct; + args = + (_ as lhs) + :: (Nolabel, {pexp_loc = fn_loc; pexp_desc = Pexp_ident _}) + :: _; + }; + } + when Hashtbl.mem loc_to_deprecated_fn_call fn_loc -> ( + (* Pipe with no arguments, [1, 2, 3]->someDeprecated *) + let deprecated_info = + Hashtbl.find loc_to_deprecated_fn_call fn_loc + in + Hashtbl.remove loc_to_deprecated_fn_call fn_loc; + + match deprecated_info.migration_template with + | Some + { + pexp_desc = + Pexp_apply + {funct = template_funct; partial; transformed_jsx}; + } -> + { + exp with + pexp_desc = + Pexp_apply + { + funct; + args = [lhs; (Nolabel, template_funct)]; + partial; + transformed_jsx; + }; + } + | _ -> + (* TODO: More elaborate warnings etc *) + (* Invalid config. *) + exp) | _ -> Ast_mapper.default_mapper.expr mapper exp); } in From 2013f63282e461f3a8d2e605edf63b6347af0b99 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 19:27:30 +0200 Subject: [PATCH 05/43] comment --- tools/src/tools.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/src/tools.ml b/tools/src/tools.ml index d0dd7ea703..570c5d0732 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1414,7 +1414,8 @@ module Migrate = struct }; } when Hashtbl.mem loc_to_deprecated_fn_call fn_loc -> ( - (* Pipe with no arguments, [1, 2, 3]->someDeprecated *) + (* Pipe with no arguments, [1, 2, 3]->someDeprecated + This is the only pipe we need to handle, because the argument one is handled by the transform above. *) let deprecated_info = Hashtbl.find loc_to_deprecated_fn_call fn_loc in From 3792808670e42444baf75a8861642a272e744409 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 19:29:05 +0200 Subject: [PATCH 06/43] comment --- tools/src/tools.ml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 570c5d0732..1bb7b36a51 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1296,6 +1296,13 @@ end module StringMap = Map.Make (String) module Migrate = struct + (* + Currently, the migrate command can handle: + - Function calls, including mapping labelled/optional arguments between calls. Piped and not piped. + + It _cannot_ (among much else) handle: + - Changing position of unlabelled arguments (would be problematic with pipes etc) + *) let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = let deprecated_function_calls = deprecated_used From 8c8e6861f0f3e566de390816c6f465e7dab33c96 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 21:31:29 +0200 Subject: [PATCH 07/43] add + adapt to a few stdlib migrations --- runtime/Js_array2.res | 192 ++++++++++++++++++ runtime/Stdlib_Array.resi | 48 ++++- .../StdlibMigration_Array.res.expected | 151 ++++++++++++++ .../src/migrate/StdlibMigration_Array.res | 145 +++++++++++++ tools/src/tools.ml | 86 ++++++-- 5 files changed, 605 insertions(+), 17 deletions(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigration_Array.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_Array.res diff --git a/runtime/Js_array2.res b/runtime/Js_array2.res index 78b7afdf3a..f5b21e2ed3 100644 --- a/runtime/Js_array2.res +++ b/runtime/Js_array2.res @@ -77,6 +77,10 @@ let strArr = Js.String.castToArrayLike("abcd") Js.Array2.from(strArr) == ["a", "b", "c", "d"] ``` */ +@deprecated({ + reason: "Use `Array.fromArrayLike` instead.", + migrate: Array.fromArrayLike() +}) @val external from: array_like<'a> => array<'a> = "Array.from" @@ -96,6 +100,10 @@ let code = s => Js.String.charCodeAt(0, s) Js.Array2.fromMap(strArr, code) == [97.0, 98.0, 99.0, 100.0] ``` */ +@deprecated({ + reason: "Use `Array.fromArrayLikeWithMap` instead.", + migrate: Array.fromArrayLikeWithMap() +}) @val external fromMap: (array_like<'a>, 'a => 'b) => array<'b> = "Array.from" @@ -112,6 +120,10 @@ Js.Array2.isArray(list{5, 2, 3, 1, 4}) == true Js.Array2.isArray("abcd") == false ``` */ +@deprecated({ + reason: "Use `Array.isArray` instead.", + migrate: Array.isArray() +}) @val external isArray: 'a => bool = "Array.isArray" @@ -120,6 +132,10 @@ Returns the number of elements in the array. See [`Array.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length) on MDN. */ +@deprecated({ + reason: "Use `Array.length` instead.", + migrate: Array.length() +}) @get external length: array<'a> => int = "length" @@ -140,6 +156,10 @@ Js.Array2.copyWithin(arr, ~to_=2) == [100, 101, 100, 101, 102] arr == [100, 101, 100, 101, 102] ``` */ +@deprecated({ + reason: "Use `Array.copyAllWithin` instead.", + migrate: Array.copyAllWithin(~target=%insert.labelledArgument("to_")) +}) @send external copyWithin: (t<'a>, ~to_: int) => t<'a> = "copyWithin" @@ -160,6 +180,10 @@ Js.Array2.copyWithinFrom(arr, ~from=2, ~to_=0) == [102, 103, 104, 103, 104] arr == [102, 103, 104, 103, 104] ``` */ +@deprecated({ + reason: "Use `Array.copyWithinToEnd` instead.", + migrate: Array.copyWithinToEnd(~target=%insert.labelledArgument("to_"), ~start=%insert.labelledArgument("from")) +}) @send external copyWithinFrom: (t<'a>, ~to_: int, ~from: int) => t<'a> = "copyWithin" @@ -180,6 +204,10 @@ Js.Array2.copyWithinFromRange(arr, ~start=2, ~end_=5, ~to_=1) == [100, 102, 103, arr == [100, 102, 103, 104, 104, 105] ``` */ +@deprecated({ + reason: "Use `Array.copyWithin` instead.", + migrate: Array.copyWithin(~target=%insert.labelledArgument("to_"), ~end=%insert.labelledArgument("end_")) +}) @send external copyWithinFromRange: (t<'a>, ~to_: int, ~start: int, ~end_: int) => t<'a> = "copyWithin" @@ -202,6 +230,10 @@ Js.Array2.fillInPlace(arr, 99) == [99, 99, 99, 99, 99] arr == [99, 99, 99, 99, 99] ``` */ +@deprecated({ + reason: "Use `Array.fillAll` instead.", + migrate: Array.fillAll() +}) @send external fillInPlace: (t<'a>, 'a) => t<'a> = "fill" @@ -222,6 +254,10 @@ Js.Array2.fillFromInPlace(arr, 99, ~from=2) == [100, 101, 99, 99, 99] arr == [100, 101, 99, 99, 99] ``` */ +@deprecated({ + reason: "Use `Array.fillToEnd` instead.", + migrate: Array.fillToEnd(~start=%insert.labelledArgument("from")) +}) @send external fillFromInPlace: (t<'a>, 'a, ~from: int) => t<'a> = "fill" @@ -243,6 +279,10 @@ Js.Array2.fillRangeInPlace(arr, 99, ~start=1, ~end_=4) == [100, 99, 99, 99, 104] arr == [100, 99, 99, 99, 104] ``` */ +@deprecated({ + reason: "Use `Array.fill` instead.", + migrate: Array.fill(~end=%insert.labelledArgument("end_")) +}) @send external fillRangeInPlace: (t<'a>, 'a, ~start: int, ~end_: int) => t<'a> = "fill" @@ -266,6 +306,10 @@ let empty: array = [] Js.Array2.pop(empty) == None ``` */ +@deprecated({ + reason: "Use `Array.pop` instead.", + migrate: Array.pop() +}) @send external pop: t<'a> => option<'a> = "pop" @@ -283,6 +327,10 @@ Js.Array2.push(arr, "dog") == 4 arr == ["ant", "bee", "cat", "dog"] ``` */ +@deprecated({ + reason: "Use `Array.push` instead. Note: `Array.push` returns `unit`, not the array length.", + migrate: Array.push() +}) @send external push: (t<'a>, 'a) => int = "push" @@ -301,6 +349,10 @@ Js.Array2.pushMany(arr, ["dog", "elk"]) == 5 arr == ["ant", "bee", "cat", "dog", "elk"] ``` */ +@deprecated({ + reason: "Use `Array.pushMany` instead. Note: `Array.pushMany` returns `unit`, not the array length.", + migrate: Array.pushMany() +}) @send @variadic external pushMany: (t<'a>, array<'a>) => int = "push" @@ -318,6 +370,10 @@ Js.Array2.reverseInPlace(arr) == ["cat", "bee", "ant"] arr == ["cat", "bee", "ant"] ``` */ +@deprecated({ + reason: "Use `Array.reverse` instead.", + migrate: Array.reverse() +}) @send external reverseInPlace: t<'a> => t<'a> = "reverse" @@ -339,6 +395,10 @@ let empty: array = [] Js.Array2.shift(empty) == None ``` */ +@deprecated({ + reason: "Use `Array.shift` instead.", + migrate: Array.shift() +}) @send external shift: t<'a> => option<'a> = "shift" @@ -362,6 +422,10 @@ Js.Array2.sortInPlace(numbers) == [1, 10, 2, 20, 3, 30] numbers == [1, 10, 2, 20, 3, 30] ``` */ +@deprecated({ + reason: "Use `Array.toSorted` instead.", + migrate: Array.toSorted((a, b) => %todo_("This needs a comparator function. Use `String.compare` for strings, etc.")) +}) @send external sortInPlace: t<'a> => t<'a> = "sort" @@ -395,6 +459,10 @@ let reverseNumeric = (n1, n2) => n2 - n1 Js.Array2.sortInPlaceWith(numbers, reverseNumeric) == [30, 20, 10, 3, 2, 1] ``` */ +@deprecated({ + reason: "Use `Array.sort` instead.", + migrate: Array.sort() +}) @send external sortInPlaceWith: (t<'a>, ('a, 'a) => int) => t<'a> = "sort" @@ -474,6 +542,10 @@ Js.Array2.unshift(arr, "a") == 4 arr == ["a", "b", "c", "d"] ``` */ +@deprecated({ + reason: "Use `Array.unshift` instead.", + migrate: Array.unshift() +}) @send external unshift: (t<'a>, 'a) => int = "unshift" @@ -492,6 +564,10 @@ Js.Array2.unshiftMany(arr, ["a", "b", "c"]) == 5 arr == ["a", "b", "c", "d", "e"] ``` */ +@deprecated({ + reason: "Use `Array.unshiftMany` instead.", + migrate: Array.unshiftMany() +}) @send @variadic external unshiftMany: (t<'a>, array<'a>) => int = "unshift" @@ -512,6 +588,10 @@ on MDN. Js.Array2.concat(["a", "b"], ["c", "d", "e"]) == ["a", "b", "c", "d", "e"] ``` */ +@deprecated({ + reason: "Use `Array.concat` instead.", + migrate: Array.concat() +}) @send external concat: (t<'a>, t<'a>) => t<'a> = "concat" @@ -536,6 +616,10 @@ Js.Array2.concatMany(["a", "b", "c"], [["d", "e"], ["f", "g", "h"]]) == [ ] ``` */ +@deprecated({ + reason: "Use `Array.concatMany` instead.", + migrate: Array.concatMany() +}) @send @variadic external concatMany: (t<'a>, array>) => t<'a> = "concat" @@ -551,6 +635,10 @@ Js.Array2.includes(["a", "b", "c"], "b") == true Js.Array2.includes(["a", "b", "c"], "x") == false ``` */ +@deprecated({ + reason: "Use `Array.includes` instead.", + migrate: Array.includes() +}) @send external includes: (t<'a>, 'a) => bool = "includes" @@ -567,6 +655,10 @@ Js.Array2.indexOf([100, 101, 102, 103], 102) == 2 Js.Array2.indexOf([100, 101, 102, 103], 999) == -1 ``` */ +@deprecated({ + reason: "Use `Array.indexOf` instead.", + migrate: Array.indexOf() +}) @send external indexOf: (t<'a>, 'a) => int = "indexOf" @@ -584,6 +676,10 @@ Js.Array2.indexOfFrom(["a", "b", "a", "c", "a"], "a", ~from=3) == 4 Js.Array2.indexOfFrom(["a", "b", "a", "c", "a"], "b", ~from=2) == -1 ``` */ +@deprecated({ + reason: "Use `Array.indexOfFrom` instead.", + migrate: Array.indexOfFrom() +}) @send external indexOfFrom: (t<'a>, 'a, ~from: int) => int = "indexOf" @@ -603,6 +699,10 @@ Js.Array2.joinWith([2020, 9, 4], "/") == "2020/9/4" Js.Array2.joinWith([2.5, 3.6, 3e-2], ";") == "2.5;3.6;0.03" ``` */ +@deprecated({ + reason: "Use `Array.joinUnsafe` instead.", + migrate: Array.joinUnsafe() +}) @send external joinWith: (t<'a>, string) => string = "join" @@ -619,6 +719,10 @@ Js.Array2.lastIndexOf(["a", "b", "a", "c"], "a") == 2 Js.Array2.lastIndexOf(["a", "b", "a", "c"], "x") == -1 ``` */ +@deprecated({ + reason: "Use `Array.lastIndexOf` instead.", + migrate: Array.lastIndexOf() +}) @send external lastIndexOf: (t<'a>, 'a) => int = "lastIndexOf" @@ -636,6 +740,10 @@ Js.Array2.lastIndexOfFrom(["a", "b", "a", "c", "a", "d"], "a", ~from=3) == 2 Js.Array2.lastIndexOfFrom(["a", "b", "a", "c", "a", "d"], "c", ~from=2) == -1 ``` */ +@deprecated({ + reason: "Use `Array.lastIndexOfFrom` instead.", + migrate: Array.lastIndexOfFrom() +}) @send external lastIndexOfFrom: (t<'a>, 'a, ~from: int) => int = "lastIndexOf" @@ -655,6 +763,10 @@ Js.Array2.slice(arr, ~start=-3, ~end_=-1) == [104, 105] Js.Array2.slice(arr, ~start=9, ~end_=10) == [] ``` */ +@deprecated({ + reason: "Use `Array.slice` instead.", + migrate: Array.slice(~end=%insert.labelledArgument("end_")) +}) @send external slice: (t<'a>, ~start: int, ~end_: int) => t<'a> = "slice" @@ -664,6 +776,10 @@ Returns a copy of the entire array. Same as `Js.Array2.Slice(arr, ~start=0, [`Array.slice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) on MDN. */ +@deprecated({ + reason: "Use `Array.copy` instead.", + migrate: Array.copy() +}) @send external copy: t<'a> => t<'a> = "slice" @@ -672,6 +788,10 @@ Returns a shallow copy of the given array from the given index to the end. See [`Array.slice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) on MDN. */ +@deprecated({ + reason: "Use `Array.sliceToEnd` instead.", + migrate: Array.sliceToEnd() +}) @send external sliceFrom: (t<'a>, int) => t<'a> = "slice" @@ -689,6 +809,10 @@ Js.Array2.toString([3.5, 4.6, 7.8]) == "3.5,4.6,7.8" Js.Array2.toString(["a", "b", "c"]) == "a,b,c" ``` */ +@deprecated({ + reason: "Use `Array.toString` instead.", + migrate: Array.toString() +}) @send external toString: t<'a> => string = "toString" @@ -708,6 +832,10 @@ Js.Array2.toLocaleString([Js.Date.make()]) // returns "2020-3-19 10:52:11" for locale de_DE.utf8 ``` */ +@deprecated({ + reason: "Use `Array.toLocaleString` instead.", + migrate: Array.toLocaleString() +}) @send external toLocaleString: t<'a> => string = "toLocaleString" @@ -733,6 +861,10 @@ Js.Array2.every([6, 22, 8, 4], isEven) == true Js.Array2.every([6, 22, 7, 4], isEven) == false ``` */ +@deprecated({ + reason: "Use `Array.every` instead.", + migrate: Array.every() +}) @send external every: (t<'a>, 'a => bool) => bool = "every" @@ -755,6 +887,10 @@ Js.Array2.everyi([6, -3, 5, 8], evenIndexPositive) == true Js.Array2.everyi([6, 3, -5, 8], evenIndexPositive) == false ``` */ +@deprecated({ + reason: "Use `Array.everyWithIndex` instead.", + migrate: Array.everyWithIndex() +}) @send external everyi: (t<'a>, ('a, int) => bool) => bool = "every" @@ -772,6 +908,10 @@ let nonEmpty = s => s != "" Js.Array2.filter(["abc", "", "", "def", "ghi"], nonEmpty) == ["abc", "def", "ghi"] ``` */ +@deprecated({ + reason: "Use `Array.filter` instead.", + migrate: Array.filter() +}) @send external filter: (t<'a>, 'a => bool) => t<'a> = "filter" @@ -793,6 +933,10 @@ let positiveOddElement = (item, index) => mod(index, 2) == 1 && item > 0 Js.Array2.filteri([6, 3, 5, 8, 7, -4, 1], positiveOddElement) == [3, 8] ``` */ +@deprecated({ + reason: "Use `Array.filterWithIndex` instead.", + migrate: Array.filterWithIndex() +}) @send external filteri: (t<'a>, ('a, int) => bool) => t<'a> = "filter" @@ -810,6 +954,10 @@ Js.Array2.find([33, 22, -55, 77, -44], x => x < 0) == Some(-55) Js.Array2.find([33, 22, 55, 77, 44], x => x < 0) == None ``` */ +@deprecated({ + reason: "Use `Array.find` instead.", + migrate: Array.find() +}) @send external find: (t<'a>, 'a => bool) => option<'a> = "find" @@ -832,6 +980,10 @@ Js.Array2.findi([66, -33, 55, 88, 22], positiveOddElement) == Some(88) Js.Array2.findi([66, -33, 55, -88, 22], positiveOddElement) == None ``` */ +@deprecated({ + reason: "Use `Array.findWithIndex` instead.", + migrate: Array.findWithIndex() +}) @send external findi: (t<'a>, ('a, int) => bool) => option<'a> = "find" @@ -850,6 +1002,10 @@ Js.Array2.findIndex([33, 22, -55, 77, -44], x => x < 0) == 2 Js.Array2.findIndex([33, 22, 55, 77, 44], x => x < 0) == -1 ``` */ +@deprecated({ + reason: "Use `Array.findIndex` instead.", + migrate: Array.findIndex() +}) @send external findIndex: (t<'a>, 'a => bool) => int = "findIndex" @@ -872,6 +1028,10 @@ Js.Array2.findIndexi([66, -33, 55, 88, 22], positiveOddElement) == 3 Js.Array2.findIndexi([66, -33, 55, -88, 22], positiveOddElement) == -1 ``` */ +@deprecated({ + reason: "Use `Array.findIndexWithIndex` instead.", + migrate: Array.findIndexWithIndex() +}) @send external findIndexi: (t<'a>, ('a, int) => bool) => int = "findIndex" @@ -893,6 +1053,10 @@ on MDN. Js.Array2.forEach(["a", "b", "c"], x => Js.log(x)) == () ``` */ +@deprecated({ + reason: "Use `Array.forEach` instead.", + migrate: Array.forEach() +}) @send external forEach: (t<'a>, 'a => unit) => unit = "forEach" @@ -913,6 +1077,10 @@ on MDN. Js.Array2.forEachi(["a", "b", "c"], (item, index) => Js.log2(index + 1, item)) == () ``` */ +@deprecated({ + reason: "Use `Array.forEachWithIndex` instead.", + migrate: Array.forEachWithIndex() +}) @send external forEachi: (t<'a>, ('a, int) => unit) => unit = "forEach" @@ -934,6 +1102,10 @@ Js.Array2.map([12, 4, 8], x => x * x) == [144, 16, 64] Js.Array2.map(["animal", "vegetable", "mineral"], Js.String.length) == [6, 9, 7] ``` */ +@deprecated({ + reason: "Use `Array.map` instead.", + migrate: Array.map() +}) @send external map: (t<'a>, 'a => 'b) => t<'b> = "map" @@ -953,6 +1125,10 @@ let product = (item, index) => item * index Js.Array2.mapi([10, 11, 12], product) == [0, 11, 24] ``` */ +@deprecated({ + reason: "Use `Array.mapWithIndex` instead.", + migrate: Array.mapWithIndex() +}) @send external mapi: (t<'a>, ('a, int) => 'b) => t<'b> = "map" @@ -1107,6 +1283,10 @@ Js.Array2.some([3, 7, 5, 2, 9], isEven) == true Js.Array2.some([3, 7, 5, 1, 9], isEven) == false ``` */ +@deprecated({ + reason: "Use `Array.some` instead.", + migrate: Array.some() +}) @send external some: (t<'a>, 'a => bool) => bool = "some" @@ -1130,6 +1310,10 @@ Js.Array2.somei(["ab", "cd", "ef", "gh"], sameLength) == true Js.Array2.somei(["a", "bc", "def", "gh"], sameLength) == false ``` */ +@deprecated({ + reason: "Use `Array.someWithIndex` instead.", + migrate: Array.someWithIndex() +}) @send external somei: (t<'a>, ('a, int) => bool) => bool = "some" @@ -1149,6 +1333,10 @@ Js.Array2.unsafe_get(arr, 3) == 103 Js.Array2.unsafe_get(arr, 4) // returns undefined ``` */ +@deprecated({ + reason: "Use `Array.getUnsafe` instead.", + migrate: Array.getUnsafe() +}) external unsafe_get: (array<'a>, int) => 'a = "%array_unsafe_get" /** @@ -1174,4 +1362,8 @@ Js.Array2.unsafe_set(arr, -1, 66) // you don't want to know. ``` */ +@deprecated({ + reason: "Use `Array.setUnsafe` instead.", + migrate: Array.setUnsafe() +}) external unsafe_set: (array<'a>, int, 'a) => unit = "%array_unsafe_set" diff --git a/runtime/Stdlib_Array.resi b/runtime/Stdlib_Array.resi index 8eb31682b4..c8f19770b7 100644 --- a/runtime/Stdlib_Array.resi +++ b/runtime/Stdlib_Array.resi @@ -80,14 +80,56 @@ someArray->Array.length == 2 @get external length: array<'a> => int = "length" -// TODO: Docs +/** +`copyAllWithin(array, ~target)` copies from the first element in the given array to the designated `target` position, returning the resulting array. + +Beware this will *mutate* the array. + +See [`Array.copyWithin`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) on MDN. + +## Examples + +```rescript +let arr = [100, 101, 102, 103, 104] +arr->Array.copyAllWithin(~target=2) == [100, 101, 100, 101, 102] +arr == [100, 101, 100, 101, 102] +``` +*/ @send external copyAllWithin: (array<'a>, ~target: int) => array<'a> = "copyWithin" -// TODO: Docs +/** +`copyWithinToEnd(array, ~target, ~start)` copies starting at element `start` in the given array to the designated `target` position, returning the resulting array. + +Beware this will *mutate* the array. + +See [`Array.copyWithin`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) on MDN. + +## Examples + +```rescript +let arr = [100, 101, 102, 103, 104] +arr->Array.copyWithinToEnd(~target=0, ~start=2) == [102, 103, 104, 103, 104] +arr == [102, 103, 104, 103, 104] +``` +*/ @send external copyWithinToEnd: (array<'a>, ~target: int, ~start: int) => array<'a> = "copyWithin" -// TODO: Docs +/** +`copyWithin(array, ~target, ~start, ~end)` copies starting at element `start` in the given array up to but not including `end` to the designated `target` position, returning the resulting array. + +Beware this will *mutate* the array. + +See [`Array.copyWithin`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) on MDN. + +## Examples + +```rescript +let arr = [100, 101, 102, 103, 104, 105] +arr->Array.copyWithin(~target=1, ~start=2, ~end=5) == [100, 102, 103, 104, 104, 105] +arr == [100, 102, 103, 104, 104, 105] +``` +*/ @send external copyWithin: (array<'a>, ~target: int, ~start: int, ~end: int) => array<'a> = "copyWithin" diff --git a/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected new file mode 100644 index 0000000000..5580c66da5 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected @@ -0,0 +1,151 @@ +let shift1 = [1, 2, 3]->Array.shift +let shift2 = Array.shift([1, 2, 3]) + +let slice1 = [1, 2, 3]->Array.slice(~start=1, ~end=2) +let slice2 = Array.slice([1, 2, 3], ~start=1, ~end=2) + +external someArrayLike: Js_array2.array_like = "whatever" + +let from1 = someArrayLike->Array.fromArrayLike +let from2 = Array.fromArrayLike(someArrayLike) + +let fromMap1 = someArrayLike->Array.fromArrayLikeWithMap(s => s ++ "!") +let fromMap2 = Array.fromArrayLikeWithMap(someArrayLike, s => s ++ "!") + +let isArray1 = [1, 2, 3]->Array.isArray +let isArray2 = Array.isArray([1, 2, 3]) + +let length1 = [1, 2, 3]->Array.length +let length2 = Array.length([1, 2, 3]) + +let fillInPlace1 = [1, 2, 3]->Array.fillAll(0) +let fillInPlace2 = Array.fillAll([1, 2, 3], 0) + +let fillFromInPlace1 = [1, 2, 3, 4]->Array.fillToEnd(0, ~start=2) +let fillFromInPlace2 = Array.fillToEnd([1, 2, 3, 4], 0, ~start=2) + +let fillRangeInPlace1 = [1, 2, 3, 4]->Array.fill(0, ~start=1, ~end=3) +let fillRangeInPlace2 = Array.fill([1, 2, 3, 4], 0, ~start=1, ~end=3) + +let pop1 = [1, 2, 3]->Array.pop +let pop2 = Array.pop([1, 2, 3]) + +let reverseInPlace1 = [1, 2, 3]->Array.reverse +let reverseInPlace2 = Array.reverse([1, 2, 3]) + +let concat1 = [1, 2]->Array.concat([3, 4]) +let concat2 = Array.concat([1, 2], [3, 4]) + +let concatMany1 = [1, 2]->Array.concatMany([[3, 4], [5, 6]]) +let concatMany2 = Array.concatMany([1, 2], [[3, 4], [5, 6]]) + +let includes1 = [1, 2, 3]->Array.includes(2) +let includes2 = Array.includes([1, 2, 3], 2) + +let indexOf1 = [1, 2, 3]->Array.indexOf(2) +let indexOf2 = Array.indexOf([1, 2, 3], 2) + +let indexOfFrom1 = [1, 2, 1, 3]->Array.indexOfFrom(1, ~from=2) +let indexOfFrom2 = Array.indexOfFrom([1, 2, 1, 3], 1, ~from=2) + +let joinWith1 = [1, 2, 3]->Array.joinUnsafe(",") +let joinWith2 = Array.joinUnsafe([1, 2, 3], ",") + +let lastIndexOf1 = [1, 2, 1, 3]->Array.lastIndexOf(1) +let lastIndexOf2 = Array.lastIndexOf([1, 2, 1, 3], 1) + +let lastIndexOfFrom1 = [1, 2, 1, 3, 1]->Array.lastIndexOfFrom(1, ~from=3) +let lastIndexOfFrom2 = Array.lastIndexOfFrom([1, 2, 1, 3, 1], 1, ~from=3) + +let copy1 = [1, 2, 3]->Array.copy +let copy2 = Array.copy([1, 2, 3]) + +let sliceFrom1 = [1, 2, 3, 4]->Array.sliceToEnd(2) +let sliceFrom2 = Array.sliceToEnd([1, 2, 3, 4], 2) + +let toString1 = [1, 2, 3]->Array.toString +let toString2 = Array.toString([1, 2, 3]) + +let toLocaleString1 = [1, 2, 3]->Array.toLocaleString +let toLocaleString2 = Array.toLocaleString([1, 2, 3]) + +let every1 = [2, 4, 6]->Array.every(x => mod(x, 2) == 0) +let every2 = Array.every([2, 4, 6], x => mod(x, 2) == 0) + +let everyi1 = [0, 1, 2]->Array.everyWithIndex((x, i) => x == i) +let everyi2 = Array.everyWithIndex([0, 1, 2], (x, i) => x == i) + +let filter1 = [1, 2, 3, 4]->Array.filter(x => x > 2) +let filter2 = Array.filter([1, 2, 3, 4], x => x > 2) + +let filteri1 = [0, 1, 2, 3]->Array.filterWithIndex((x, i) => i > 1) +let filteri2 = Array.filterWithIndex([0, 1, 2, 3], (x, i) => i > 1) + +let find1 = [1, 2, 3, 4]->Array.find(x => x > 2) +let find2 = Array.find([1, 2, 3, 4], x => x > 2) + +let findi1 = [0, 1, 2, 3]->Array.findWithIndex((x, i) => i > 1) +let findi2 = Array.findWithIndex([0, 1, 2, 3], (x, i) => i > 1) + +let findIndex1 = [1, 2, 3, 4]->Array.findIndex(x => x > 2) +let findIndex2 = Array.findIndex([1, 2, 3, 4], x => x > 2) + +let findIndexi1 = [0, 1, 2, 3]->Array.findIndexWithIndex((x, i) => i > 1) +let findIndexi2 = Array.findIndexWithIndex([0, 1, 2, 3], (x, i) => i > 1) + +let forEach1 = [1, 2, 3]->Array.forEach(x => ignore(x)) +let forEach2 = Array.forEach([1, 2, 3], x => ignore(x)) + +let forEachi1 = [1, 2, 3]->Array.forEachWithIndex((x, i) => ignore(x + i)) +let forEachi2 = Array.forEachWithIndex([1, 2, 3], (x, i) => ignore(x + i)) + +let map1 = [1, 2, 3]->Array.map(x => x * 2) +let map2 = Array.map([1, 2, 3], x => x * 2) + +let mapi1 = [1, 2, 3]->Array.mapWithIndex((x, i) => x + i) +let mapi2 = Array.mapWithIndex([1, 2, 3], (x, i) => x + i) + +let some1 = [1, 2, 3, 4]->Array.some(x => x > 3) +let some2 = Array.some([1, 2, 3, 4], x => x > 3) + +let somei1 = [0, 1, 2, 3]->Array.someWithIndex((x, i) => i > 2) +let somei2 = Array.someWithIndex([0, 1, 2, 3], (x, i) => i > 2) + +let unsafeGet1 = [1, 2, 3]->Array.getUnsafe(1) +let unsafeGet2 = Array.getUnsafe([1, 2, 3], 1) + +let unsafeSet1 = [1, 2, 3]->Array.setUnsafe(1, 5) +let unsafeSet2 = Array.setUnsafe([1, 2, 3], 1, 5) + +let copyWithin1 = [1, 2, 3, 4, 5]->Array.copyAllWithin(~target=2) +let copyWithin2 = Array.copyAllWithin([1, 2, 3, 4, 5], ~target=2) + +let copyWithinFrom1 = [1, 2, 3, 4, 5]->Array.copyWithinToEnd(~target=0, ~start=2) +let copyWithinFrom2 = Array.copyWithinToEnd([1, 2, 3, 4, 5], ~target=0, ~start=2) + +let copyWithinFromRange1 = [1, 2, 3, 4, 5, 6]->Array.copyWithin(~target=1, ~start=2, ~end=5) +let copyWithinFromRange2 = Array.copyWithin([1, 2, 3, 4, 5, 6], ~target=1, ~start=2, ~end=5) + +let push1 = [1, 2, 3]->Array.push(4) +let push2 = Array.push([1, 2, 3], 4) + +let pushMany1 = [1, 2, 3]->Array.pushMany([4, 5]) +let pushMany2 = Array.pushMany([1, 2, 3], [4, 5]) + +let sortInPlace1 = + ["c", "a", "b"]->Array.toSorted((a, b) => + %todo("This needs a comparator function. Use `String.compare` for strings, etc.") + ) +let sortInPlace2 = Array.toSorted(["c", "a", "b"], (a, b) => + %todo("This needs a comparator function. Use `String.compare` for strings, etc.") +) + +let sortInPlaceWith1 = [3, 1, 2]->Array.sort((a, b) => a - b) +let sortInPlaceWith2 = Array.sort([3, 1, 2], (a, b) => a - b) + +let unshift1 = [1, 2, 3]->Array.unshift(4) +let unshift2 = Array.unshift([1, 2, 3], 4) + +let unshiftMany1 = [1, 2, 3]->Array.unshiftMany([4, 5]) +let unshiftMany2 = Array.unshiftMany([1, 2, 3], [4, 5]) + diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Array.res b/tests/tools_tests/src/migrate/StdlibMigration_Array.res new file mode 100644 index 0000000000..e8d54e6dab --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_Array.res @@ -0,0 +1,145 @@ +let shift1 = [1, 2, 3]->Js.Array2.shift +let shift2 = Js.Array2.shift([1, 2, 3]) + +let slice1 = [1,2,3]->Js.Array2.slice(~start=1, ~end_=2) +let slice2 = Js.Array2.slice([1,2,3], ~start=1, ~end_=2) + +external someArrayLike: Js_array2.array_like = "whatever" + +let from1 = someArrayLike->Js.Array2.from +let from2 = Js.Array2.from(someArrayLike) + +let fromMap1 = someArrayLike->Js.Array2.fromMap(s => s ++ "!") +let fromMap2 = Js.Array2.fromMap(someArrayLike, s => s ++ "!") + +let isArray1 = [1, 2, 3]->Js.Array2.isArray +let isArray2 = Js.Array2.isArray([1, 2, 3]) + +let length1 = [1, 2, 3]->Js.Array2.length +let length2 = Js.Array2.length([1, 2, 3]) + +let fillInPlace1 = [1, 2, 3]->Js.Array2.fillInPlace(0) +let fillInPlace2 = Js.Array2.fillInPlace([1, 2, 3], 0) + +let fillFromInPlace1 = [1, 2, 3, 4]->Js.Array2.fillFromInPlace(0, ~from=2) +let fillFromInPlace2 = Js.Array2.fillFromInPlace([1, 2, 3, 4], 0, ~from=2) + +let fillRangeInPlace1 = [1, 2, 3, 4]->Js.Array2.fillRangeInPlace(0, ~start=1, ~end_=3) +let fillRangeInPlace2 = Js.Array2.fillRangeInPlace([1, 2, 3, 4], 0, ~start=1, ~end_=3) + +let pop1 = [1, 2, 3]->Js.Array2.pop +let pop2 = Js.Array2.pop([1, 2, 3]) + +let reverseInPlace1 = [1, 2, 3]->Js.Array2.reverseInPlace +let reverseInPlace2 = Js.Array2.reverseInPlace([1, 2, 3]) + +let concat1 = [1, 2]->Js.Array2.concat([3, 4]) +let concat2 = Js.Array2.concat([1, 2], [3, 4]) + +let concatMany1 = [1, 2]->Js.Array2.concatMany([[3, 4], [5, 6]]) +let concatMany2 = Js.Array2.concatMany([1, 2], [[3, 4], [5, 6]]) + +let includes1 = [1, 2, 3]->Js.Array2.includes(2) +let includes2 = Js.Array2.includes([1, 2, 3], 2) + +let indexOf1 = [1, 2, 3]->Js.Array2.indexOf(2) +let indexOf2 = Js.Array2.indexOf([1, 2, 3], 2) + +let indexOfFrom1 = [1, 2, 1, 3]->Js.Array2.indexOfFrom(1, ~from=2) +let indexOfFrom2 = Js.Array2.indexOfFrom([1, 2, 1, 3], 1, ~from=2) + +let joinWith1 = [1, 2, 3]->Js.Array2.joinWith(",") +let joinWith2 = Js.Array2.joinWith([1, 2, 3], ",") + +let lastIndexOf1 = [1, 2, 1, 3]->Js.Array2.lastIndexOf(1) +let lastIndexOf2 = Js.Array2.lastIndexOf([1, 2, 1, 3], 1) + +let lastIndexOfFrom1 = [1, 2, 1, 3, 1]->Js.Array2.lastIndexOfFrom(1, ~from=3) +let lastIndexOfFrom2 = Js.Array2.lastIndexOfFrom([1, 2, 1, 3, 1], 1, ~from=3) + +let copy1 = [1, 2, 3]->Js.Array2.copy +let copy2 = Js.Array2.copy([1, 2, 3]) + +let sliceFrom1 = [1, 2, 3, 4]->Js.Array2.sliceFrom(2) +let sliceFrom2 = Js.Array2.sliceFrom([1, 2, 3, 4], 2) + +let toString1 = [1, 2, 3]->Js.Array2.toString +let toString2 = Js.Array2.toString([1, 2, 3]) + +let toLocaleString1 = [1, 2, 3]->Js.Array2.toLocaleString +let toLocaleString2 = Js.Array2.toLocaleString([1, 2, 3]) + +let every1 = [2, 4, 6]->Js.Array2.every(x => mod(x, 2) == 0) +let every2 = Js.Array2.every([2, 4, 6], x => mod(x, 2) == 0) + +let everyi1 = [0, 1, 2]->Js.Array2.everyi((x, i) => x == i) +let everyi2 = Js.Array2.everyi([0, 1, 2], (x, i) => x == i) + +let filter1 = [1, 2, 3, 4]->Js.Array2.filter(x => x > 2) +let filter2 = Js.Array2.filter([1, 2, 3, 4], x => x > 2) + +let filteri1 = [0, 1, 2, 3]->Js.Array2.filteri((x, i) => i > 1) +let filteri2 = Js.Array2.filteri([0, 1, 2, 3], (x, i) => i > 1) + +let find1 = [1, 2, 3, 4]->Js.Array2.find(x => x > 2) +let find2 = Js.Array2.find([1, 2, 3, 4], x => x > 2) + +let findi1 = [0, 1, 2, 3]->Js.Array2.findi((x, i) => i > 1) +let findi2 = Js.Array2.findi([0, 1, 2, 3], (x, i) => i > 1) + +let findIndex1 = [1, 2, 3, 4]->Js.Array2.findIndex(x => x > 2) +let findIndex2 = Js.Array2.findIndex([1, 2, 3, 4], x => x > 2) + +let findIndexi1 = [0, 1, 2, 3]->Js.Array2.findIndexi((x, i) => i > 1) +let findIndexi2 = Js.Array2.findIndexi([0, 1, 2, 3], (x, i) => i > 1) + +let forEach1 = [1, 2, 3]->Js.Array2.forEach(x => ignore(x)) +let forEach2 = Js.Array2.forEach([1, 2, 3], x => ignore(x)) + +let forEachi1 = [1, 2, 3]->Js.Array2.forEachi((x, i) => ignore(x + i)) +let forEachi2 = Js.Array2.forEachi([1, 2, 3], (x, i) => ignore(x + i)) + +let map1 = [1, 2, 3]->Js.Array2.map(x => x * 2) +let map2 = Js.Array2.map([1, 2, 3], x => x * 2) + +let mapi1 = [1, 2, 3]->Js.Array2.mapi((x, i) => x + i) +let mapi2 = Js.Array2.mapi([1, 2, 3], (x, i) => x + i) + +let some1 = [1, 2, 3, 4]->Js.Array2.some(x => x > 3) +let some2 = Js.Array2.some([1, 2, 3, 4], x => x > 3) + +let somei1 = [0, 1, 2, 3]->Js.Array2.somei((x, i) => i > 2) +let somei2 = Js.Array2.somei([0, 1, 2, 3], (x, i) => i > 2) + +let unsafeGet1 = [1, 2, 3]->Js.Array2.unsafe_get(1) +let unsafeGet2 = Js.Array2.unsafe_get([1, 2, 3], 1) + +let unsafeSet1 = [1, 2, 3]->Js.Array2.unsafe_set(1, 5) +let unsafeSet2 = Js.Array2.unsafe_set([1, 2, 3], 1, 5) + +let copyWithin1 = [1, 2, 3, 4, 5]->Js.Array2.copyWithin(~to_=2) +let copyWithin2 = Js.Array2.copyWithin([1, 2, 3, 4, 5], ~to_=2) + +let copyWithinFrom1 = [1, 2, 3, 4, 5]->Js.Array2.copyWithinFrom(~to_=0, ~from=2) +let copyWithinFrom2 = Js.Array2.copyWithinFrom([1, 2, 3, 4, 5], ~to_=0, ~from=2) + +let copyWithinFromRange1 = [1, 2, 3, 4, 5, 6]->Js.Array2.copyWithinFromRange(~to_=1, ~start=2, ~end_=5) +let copyWithinFromRange2 = Js.Array2.copyWithinFromRange([1, 2, 3, 4, 5, 6], ~to_=1, ~start=2, ~end_=5) + +let push1 = [1, 2, 3]->Js.Array2.push(4) +let push2 = Js.Array2.push([1, 2, 3], 4) + +let pushMany1 = [1, 2, 3]->Js.Array2.pushMany([4, 5]) +let pushMany2 = Js.Array2.pushMany([1, 2, 3], [4, 5]) + +let sortInPlace1 = ["c", "a", "b"]->Js.Array2.sortInPlace +let sortInPlace2 = Js.Array2.sortInPlace(["c", "a", "b"]) + +let sortInPlaceWith1 = [3, 1, 2]->Js.Array2.sortInPlaceWith((a, b) => a - b) +let sortInPlaceWith2 = Js.Array2.sortInPlaceWith([3, 1, 2], (a, b) => a - b) + +let unshift1 = [1, 2, 3]->Js.Array2.unshift(4) +let unshift2 = Js.Array2.unshift([1, 2, 3], 4) + +let unshiftMany1 = [1, 2, 3]->Js.Array2.unshiftMany([4, 5]) +let unshiftMany2 = Js.Array2.unshiftMany([1, 2, 3], [4, 5]) diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 1bb7b36a51..e4c6af6b3c 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1303,6 +1303,25 @@ module Migrate = struct It _cannot_ (among much else) handle: - Changing position of unlabelled arguments (would be problematic with pipes etc) *) + + (* + TODO: + - Migrate unlabelled arguments (Array.reduceXxx) + - Migrate type usage (Js.Array2.t -> array, etc) + *) + + module MapperUtils = struct + let get_template_args_to_insert mapper template_args = + template_args + |> List.filter_map (fun (label, arg) -> + match arg with + | {Parsetree.pexp_desc = Pexp_extension ({txt}, _)} + when String.starts_with txt ~prefix:"insert." -> + None + | {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)} -> None + | _ -> Some (label, mapper.Ast_mapper.expr mapper arg)) + end + let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = let deprecated_function_calls = deprecated_used @@ -1320,6 +1339,14 @@ module Migrate = struct let mapper = { Ast_mapper.default_mapper with + extension = + (fun mapper ext -> + (* Map back %todo_ to %todo, since using %todo directly would result in errors where we don't want them. *) + match ext with + | ({txt = "todo_"} as e), payload -> + Ast_mapper.default_mapper.extension mapper + ({e with txt = "todo"}, payload) + | e -> Ast_mapper.default_mapper.extension mapper e); expr = (fun mapper exp -> match exp with @@ -1381,6 +1408,9 @@ module Migrate = struct | _ -> None) |> StringMap.of_list in + let template_args_to_insert = + MapperUtils.get_template_args_to_insert mapper template_args + in { exp with pexp_desc = @@ -1388,7 +1418,7 @@ module Migrate = struct { funct = template_funct; args = - source_args + (source_args |> List.map (fun (label, arg) -> match label with | Asttypes.Labelled {loc; txt = label} @@ -1400,7 +1430,8 @@ module Migrate = struct ( Asttypes.Labelled {loc; txt = mapped_label_name}, arg ) - | label -> (label, arg)); + | label -> (label, arg))) + @ template_args_to_insert; partial; transformed_jsx; }; @@ -1433,19 +1464,46 @@ module Migrate = struct { pexp_desc = Pexp_apply - {funct = template_funct; partial; transformed_jsx}; + { + funct = template_funct; + args = template_args; + partial; + transformed_jsx; + }; } -> - { - exp with - pexp_desc = - Pexp_apply - { - funct; - args = [lhs; (Nolabel, template_funct)]; - partial; - transformed_jsx; - }; - } + let template_args_to_insert = + MapperUtils.get_template_args_to_insert mapper template_args + in + if List.is_empty template_args_to_insert then + { + exp with + pexp_desc = + Pexp_apply + { + funct; + args = [lhs; (Nolabel, template_funct)]; + partial; + transformed_jsx; + }; + } + else + { + exp with + pexp_desc = + Pexp_apply + { + funct; + args = + [ + lhs; + ( Nolabel, + Ast_helper.Exp.apply template_funct + template_args_to_insert ); + ]; + partial; + transformed_jsx; + }; + } | _ -> (* TODO: More elaborate warnings etc *) (* Invalid config. *) From c582868188f4c8ecb677c22459dc95592044846d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 22:13:26 +0200 Subject: [PATCH 08/43] handle mapping labelled arg to unlabelled --- runtime/Js_array2.res | 4 +- tests/tools_tests/Makefile | 1 + tests/tools_tests/package.json | 2 +- .../StdlibMigration_Array.res.expected | 24 +-- .../src/migrate/StdlibMigration_Array.res | 16 +- tests/tools_tests/test.sh | 7 + tools/src/tools.ml | 137 ++++++++++++------ 7 files changed, 126 insertions(+), 65 deletions(-) diff --git a/runtime/Js_array2.res b/runtime/Js_array2.res index f5b21e2ed3..4bb86844b3 100644 --- a/runtime/Js_array2.res +++ b/runtime/Js_array2.res @@ -678,7 +678,7 @@ Js.Array2.indexOfFrom(["a", "b", "a", "c", "a"], "b", ~from=2) == -1 */ @deprecated({ reason: "Use `Array.indexOfFrom` instead.", - migrate: Array.indexOfFrom() + migrate: Array.indexOfFrom(%insert.labelledArgument("from")) }) @send external indexOfFrom: (t<'a>, 'a, ~from: int) => int = "indexOf" @@ -742,7 +742,7 @@ Js.Array2.lastIndexOfFrom(["a", "b", "a", "c", "a", "d"], "c", ~from=2) == -1 */ @deprecated({ reason: "Use `Array.lastIndexOfFrom` instead.", - migrate: Array.lastIndexOfFrom() + migrate: Array.lastIndexOfFrom(%insert.labelledArgument("from")) }) @send external lastIndexOfFrom: (t<'a>, 'a, ~from: int) => int = "lastIndexOf" diff --git a/tests/tools_tests/Makefile b/tests/tools_tests/Makefile index 4d26df85bb..e8d2c1f3fc 100644 --- a/tests/tools_tests/Makefile +++ b/tests/tools_tests/Makefile @@ -5,6 +5,7 @@ build: test: build ./test.sh + yarn build clean: yarn clean diff --git a/tests/tools_tests/package.json b/tests/tools_tests/package.json index 2bf38e72e8..63fd369df9 100644 --- a/tests/tools_tests/package.json +++ b/tests/tools_tests/package.json @@ -2,7 +2,7 @@ "name": "@tests/tools", "private": true, "scripts": { - "build": "rescript legacy build", + "build": "rescript legacy build -warn-error -3", "clean": "rescript clean", "dev": "rescript -w" }, diff --git a/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected index 5580c66da5..6115327e82 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected @@ -45,8 +45,8 @@ let includes2 = Array.includes([1, 2, 3], 2) let indexOf1 = [1, 2, 3]->Array.indexOf(2) let indexOf2 = Array.indexOf([1, 2, 3], 2) -let indexOfFrom1 = [1, 2, 1, 3]->Array.indexOfFrom(1, ~from=2) -let indexOfFrom2 = Array.indexOfFrom([1, 2, 1, 3], 1, ~from=2) +let indexOfFrom1 = [1, 2, 1, 3]->Array.indexOfFrom(1, 2) +let indexOfFrom2 = Array.indexOfFrom([1, 2, 1, 3], 1, 2) let joinWith1 = [1, 2, 3]->Array.joinUnsafe(",") let joinWith2 = Array.joinUnsafe([1, 2, 3], ",") @@ -54,8 +54,8 @@ let joinWith2 = Array.joinUnsafe([1, 2, 3], ",") let lastIndexOf1 = [1, 2, 1, 3]->Array.lastIndexOf(1) let lastIndexOf2 = Array.lastIndexOf([1, 2, 1, 3], 1) -let lastIndexOfFrom1 = [1, 2, 1, 3, 1]->Array.lastIndexOfFrom(1, ~from=3) -let lastIndexOfFrom2 = Array.lastIndexOfFrom([1, 2, 1, 3, 1], 1, ~from=3) +let lastIndexOfFrom1 = [1, 2, 1, 3, 1]->Array.lastIndexOfFrom(1, 3) +let lastIndexOfFrom2 = Array.lastIndexOfFrom([1, 2, 1, 3, 1], 1, 3) let copy1 = [1, 2, 3]->Array.copy let copy2 = Array.copy([1, 2, 3]) @@ -78,20 +78,20 @@ let everyi2 = Array.everyWithIndex([0, 1, 2], (x, i) => x == i) let filter1 = [1, 2, 3, 4]->Array.filter(x => x > 2) let filter2 = Array.filter([1, 2, 3, 4], x => x > 2) -let filteri1 = [0, 1, 2, 3]->Array.filterWithIndex((x, i) => i > 1) -let filteri2 = Array.filterWithIndex([0, 1, 2, 3], (x, i) => i > 1) +let filteri1 = [0, 1, 2, 3]->Array.filterWithIndex((_x, i) => i > 1) +let filteri2 = Array.filterWithIndex([0, 1, 2, 3], (_x, i) => i > 1) let find1 = [1, 2, 3, 4]->Array.find(x => x > 2) let find2 = Array.find([1, 2, 3, 4], x => x > 2) -let findi1 = [0, 1, 2, 3]->Array.findWithIndex((x, i) => i > 1) -let findi2 = Array.findWithIndex([0, 1, 2, 3], (x, i) => i > 1) +let findi1 = [0, 1, 2, 3]->Array.findWithIndex((_x, i) => i > 1) +let findi2 = Array.findWithIndex([0, 1, 2, 3], (_x, i) => i > 1) let findIndex1 = [1, 2, 3, 4]->Array.findIndex(x => x > 2) let findIndex2 = Array.findIndex([1, 2, 3, 4], x => x > 2) -let findIndexi1 = [0, 1, 2, 3]->Array.findIndexWithIndex((x, i) => i > 1) -let findIndexi2 = Array.findIndexWithIndex([0, 1, 2, 3], (x, i) => i > 1) +let findIndexi1 = [0, 1, 2, 3]->Array.findIndexWithIndex((_x, i) => i > 1) +let findIndexi2 = Array.findIndexWithIndex([0, 1, 2, 3], (_x, i) => i > 1) let forEach1 = [1, 2, 3]->Array.forEach(x => ignore(x)) let forEach2 = Array.forEach([1, 2, 3], x => ignore(x)) @@ -108,8 +108,8 @@ let mapi2 = Array.mapWithIndex([1, 2, 3], (x, i) => x + i) let some1 = [1, 2, 3, 4]->Array.some(x => x > 3) let some2 = Array.some([1, 2, 3, 4], x => x > 3) -let somei1 = [0, 1, 2, 3]->Array.someWithIndex((x, i) => i > 2) -let somei2 = Array.someWithIndex([0, 1, 2, 3], (x, i) => i > 2) +let somei1 = [0, 1, 2, 3]->Array.someWithIndex((_x, i) => i > 2) +let somei2 = Array.someWithIndex([0, 1, 2, 3], (_x, i) => i > 2) let unsafeGet1 = [1, 2, 3]->Array.getUnsafe(1) let unsafeGet2 = Array.getUnsafe([1, 2, 3], 1) diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Array.res b/tests/tools_tests/src/migrate/StdlibMigration_Array.res index e8d54e6dab..4882cb8c91 100644 --- a/tests/tools_tests/src/migrate/StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/StdlibMigration_Array.res @@ -78,20 +78,20 @@ let everyi2 = Js.Array2.everyi([0, 1, 2], (x, i) => x == i) let filter1 = [1, 2, 3, 4]->Js.Array2.filter(x => x > 2) let filter2 = Js.Array2.filter([1, 2, 3, 4], x => x > 2) -let filteri1 = [0, 1, 2, 3]->Js.Array2.filteri((x, i) => i > 1) -let filteri2 = Js.Array2.filteri([0, 1, 2, 3], (x, i) => i > 1) +let filteri1 = [0, 1, 2, 3]->Js.Array2.filteri((_x, i) => i > 1) +let filteri2 = Js.Array2.filteri([0, 1, 2, 3], (_x, i) => i > 1) let find1 = [1, 2, 3, 4]->Js.Array2.find(x => x > 2) let find2 = Js.Array2.find([1, 2, 3, 4], x => x > 2) -let findi1 = [0, 1, 2, 3]->Js.Array2.findi((x, i) => i > 1) -let findi2 = Js.Array2.findi([0, 1, 2, 3], (x, i) => i > 1) +let findi1 = [0, 1, 2, 3]->Js.Array2.findi((_x, i) => i > 1) +let findi2 = Js.Array2.findi([0, 1, 2, 3], (_x, i) => i > 1) let findIndex1 = [1, 2, 3, 4]->Js.Array2.findIndex(x => x > 2) let findIndex2 = Js.Array2.findIndex([1, 2, 3, 4], x => x > 2) -let findIndexi1 = [0, 1, 2, 3]->Js.Array2.findIndexi((x, i) => i > 1) -let findIndexi2 = Js.Array2.findIndexi([0, 1, 2, 3], (x, i) => i > 1) +let findIndexi1 = [0, 1, 2, 3]->Js.Array2.findIndexi((_x, i) => i > 1) +let findIndexi2 = Js.Array2.findIndexi([0, 1, 2, 3], (_x, i) => i > 1) let forEach1 = [1, 2, 3]->Js.Array2.forEach(x => ignore(x)) let forEach2 = Js.Array2.forEach([1, 2, 3], x => ignore(x)) @@ -108,8 +108,8 @@ let mapi2 = Js.Array2.mapi([1, 2, 3], (x, i) => x + i) let some1 = [1, 2, 3, 4]->Js.Array2.some(x => x > 3) let some2 = Js.Array2.some([1, 2, 3, 4], x => x > 3) -let somei1 = [0, 1, 2, 3]->Js.Array2.somei((x, i) => i > 2) -let somei2 = Js.Array2.somei([0, 1, 2, 3], (x, i) => i > 2) +let somei1 = [0, 1, 2, 3]->Js.Array2.somei((_x, i) => i > 2) +let somei2 = Js.Array2.somei([0, 1, 2, 3], (_x, i) => i > 2) let unsafeGet1 = [1, 2, 3]->Js.Array2.unsafe_get(1) let unsafeGet2 = Js.Array2.unsafe_get([1, 2, 3], 1) diff --git a/tests/tools_tests/test.sh b/tests/tools_tests/test.sh index 700b959b57..49c2f5b1a4 100755 --- a/tests/tools_tests/test.sh +++ b/tests/tools_tests/test.sh @@ -42,6 +42,13 @@ for file in src/migrate/*.{res,resi}; do fi done +# Move migrated files to expected directory so they can be compiled in the project +#for file in src/migrate/StdlibMigration_*.res; do +# expected_file="src/expected/$(basename $file).expected" +# output="src/migrate/migrated/Migrated_$(basename $file)" +# cp -f "$expected_file" "$output" +#done + warningYellow='\033[0;33m' successGreen='\033[0;32m' reset='\033[0m' diff --git a/tools/src/tools.ml b/tools/src/tools.ml index e4c6af6b3c..083aee665e 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1311,15 +1311,86 @@ module Migrate = struct *) module MapperUtils = struct - let get_template_args_to_insert mapper template_args = + let get_template_args_to_insert mapper template_args source_args = + let labelled_args_that_will_be_mapped = ref [] in + let mapped_template_args = + template_args + |> List.filter_map (fun (label, arg) -> + match (label, arg) with + | ( Asttypes.Nolabel, + { + Parsetree.pexp_desc = + Pexp_extension + ( {txt = "insert.labelledArgument"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_constant + (Pconst_string (arg_name, _)); + }, + _ ); + }; + ] ); + } ) -> ( + (* Map unlabelled args *) + let arg_in_source_args = + source_args + |> List.find_map (fun (label, arg) -> + match label with + | Asttypes.Labelled {txt = label} + | Optional {txt = label} -> + if label = arg_name then Some arg else None + | _ -> None) + in + match arg_in_source_args with + | None -> None + | Some arg -> + labelled_args_that_will_be_mapped := + arg_name :: !labelled_args_that_will_be_mapped; + Some (Asttypes.Nolabel, arg)) + | ( _, + { + pexp_desc = + Pexp_extension ({txt = "insert.labelledArgument"}, _); + } ) -> + (* Do not add any other instance of insert.labelledArgument. *) + None + | _, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)} -> + None + | _ -> Some (label, mapper.Ast_mapper.expr mapper arg)) + in + (mapped_template_args, !labelled_args_that_will_be_mapped) + + let build_labelled_args_map template_args = template_args |> List.filter_map (fun (label, arg) -> - match arg with - | {Parsetree.pexp_desc = Pexp_extension ({txt}, _)} - when String.starts_with txt ~prefix:"insert." -> - None - | {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)} -> None - | _ -> Some (label, mapper.Ast_mapper.expr mapper arg)) + match (label, arg) with + | ( (Asttypes.Labelled {txt = label} | Optional {txt = label}), + { + Parsetree.pexp_desc = + Pexp_extension + ( {txt = "insert.labelledArgument"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_constant + (Pconst_string (arg_name, _)); + }, + _ ); + }; + ] ); + } ) -> + Some (arg_name, label) + | _ -> None) + |> StringMap.of_list end let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = @@ -1380,36 +1451,11 @@ module Migrate = struct }; } -> let labelled_args_map = - template_args - |> List.filter_map (fun (label, arg) -> - match (label, arg) with - | ( ( Asttypes.Labelled {txt = label} - | Optional {txt = label} ), - { - Parsetree.pexp_desc = - Pexp_extension - ( {txt = "insert.labelledArgument"}, - PStr - [ - { - pstr_desc = - Pstr_eval - ( { - pexp_desc = - Pexp_constant - (Pconst_string - (arg_name, _)); - }, - _ ); - }; - ] ); - } ) -> - Some (arg_name, label) - | _ -> None) - |> StringMap.of_list + MapperUtils.build_labelled_args_map template_args in - let template_args_to_insert = + let template_args_to_insert, will_be_mapped = MapperUtils.get_template_args_to_insert mapper template_args + source_args in { exp with @@ -1419,18 +1465,24 @@ module Migrate = struct funct = template_funct; args = (source_args - |> List.map (fun (label, arg) -> + |> List.filter_map (fun (label, arg) -> match label with | Asttypes.Labelled {loc; txt = label} - | Asttypes.Optional {loc; txt = label} + | Optional {loc; txt = label} when StringMap.mem label labelled_args_map -> + (* Map labelled args that has just changed name. *) let mapped_label_name = StringMap.find label labelled_args_map in - ( Asttypes.Labelled - {loc; txt = mapped_label_name}, - arg ) - | label -> (label, arg))) + Some + ( Asttypes.Labelled + {loc; txt = mapped_label_name}, + arg ) + | (Labelled {txt} | Optional {txt}) + when List.mem txt will_be_mapped -> + (* These have been mapped already, do not add. *) + None + | label -> Some (label, arg))) @ template_args_to_insert; partial; transformed_jsx; @@ -1471,8 +1523,9 @@ module Migrate = struct transformed_jsx; }; } -> - let template_args_to_insert = + let template_args_to_insert, _ = MapperUtils.get_template_args_to_insert mapper template_args + [] in if List.is_empty template_args_to_insert then { From 0ac6b33a45655f4d14467c1a0540502ca81c0ed4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:11:15 +0200 Subject: [PATCH 09/43] support unlabelled arguments as well --- runtime/Js_array2.res | 36 +++- ...tdlibMigrationNoCompile_Array.res.expected | 4 + .../StdlibMigration_Array.res.expected | 28 ++- .../StdlibMigrationNoCompile_Array.res | 3 + .../src/migrate/StdlibMigration_Array.res | 24 ++- .../Migrated_StdlibMigration_Array.res | 169 +++++++++++++++ tests/tools_tests/test.sh | 10 +- tools/src/tools.ml | 193 +++++++++++++++--- 8 files changed, 415 insertions(+), 52 deletions(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigrationNoCompile_Array.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigrationNoCompile_Array.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res diff --git a/runtime/Js_array2.res b/runtime/Js_array2.res index 4bb86844b3..e716e3075d 100644 --- a/runtime/Js_array2.res +++ b/runtime/Js_array2.res @@ -489,7 +489,10 @@ Js.Array2.spliceInPlace(arr3, ~pos=9, ~remove=2, ~add=["x", "y", "z"]) == [] arr3 == ["a", "b", "c", "d", "e", "f", "x", "y", "z"] ``` */ -@send @variadic +@send @variadic @deprecated({ + reason: "Use `Array.splice` instead.", + migrate: Array.splice(~start=%insert.labelledArgument("pos"), ~remove=%insert.labelledArgument("remove"), ~insert=%insert.labelledArgument("add")) +}) external spliceInPlace: (t<'a>, ~pos: int, ~remove: int, ~add: array<'a>) => t<'a> = "splice" /** @@ -507,7 +510,10 @@ Js.Array2.removeFromInPlace(arr, ~pos=4) == ["e", "f"] arr == ["a", "b", "c", "d"] ``` */ -@send +@send @deprecated({ + reason: "Use `Array.removeInPlace` instead.", + migrate: Array.removeInPlace(%insert.labelledArgument("pos")) +}) external removeFromInPlace: (t<'a>, ~pos: int) => t<'a> = "splice" /** @@ -525,7 +531,10 @@ Js.Array2.removeCountInPlace(arr, ~pos=2, ~count=3) == ["c", "d", "e"] arr == ["a", "b", "f"] ``` */ -@send +@send @deprecated({ + reason: "Use `Array.splice` instead.", + migrate: Array.splice(~start=%insert.labelledArgument("pos"), ~remove=%insert.labelledArgument("count"), ~insert=[]) +}) external removeCountInPlace: (t<'a>, ~pos: int, ~count: int) => t<'a> = "splice" /** @@ -790,7 +799,7 @@ on MDN. */ @deprecated({ reason: "Use `Array.sliceToEnd` instead.", - migrate: Array.sliceToEnd() + migrate: Array.sliceToEnd(~start=%insert.unlabelledArgument(1)) }) @send external sliceFrom: (t<'a>, int) => t<'a> = "slice" @@ -1163,6 +1172,10 @@ Js.Array2.reduce( Js.Array2.reduce([2.0, 4.0], (acc, item) => item /. acc, 1.0) == 2.0 // 4.0 / (2.0 / 1.0) ``` */ +@deprecated({ + reason: "Use `Array.reduce` instead.", + migrate: Array.reduce(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) +}) @send external reduce: (t<'a>, ('b, 'a) => 'b, 'b) => 'b = "reduce" @@ -1198,7 +1211,10 @@ let sumOfEvens = (accumulator, item, index) => Js.Array2.reducei([2, 5, 1, 4, 3], sumOfEvens, 0) == 6 ``` */ -@send +@send @deprecated({ + reason: "Use `Array.reduceWithIndex` instead.", + migrate: Array.reduceWithIndex(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) +}) external reducei: (t<'a>, ('b, 'a, int) => 'b, 'b) => 'b = "reduce" /** @@ -1230,7 +1246,10 @@ Js.Array2.reduceRight([10, 2, 4], sumOfSquares, 0) == 120 Js.Array2.reduceRight([2.0, 4.0], (acc, item) => item /. acc, 1.0) == 0.5 // 2.0 / (4.0 / 1.0) ``` */ -@send +@send @deprecated({ + reason: "Use `Array.reduceRight` instead.", + migrate: Array.reduceRight(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) +}) external reduceRight: (t<'a>, ('b, 'a) => 'b, 'b) => 'b = "reduceRight" /** @@ -1267,7 +1286,10 @@ let sumOfEvens = (accumulator, item, index) => Js.Array2.reduceRighti([2, 5, 1, 4, 3], sumOfEvens, 0) == 6 ``` */ -@send +@send @deprecated({ + reason: "Use `Array.reduceRightWithIndex` instead.", + migrate: Array.reduceRightWithIndex(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) +}) external reduceRighti: (t<'a>, ('b, 'a, int) => 'b, 'b) => 'b = "reduceRight" /** diff --git a/tests/tools_tests/src/expected/StdlibMigrationNoCompile_Array.res.expected b/tests/tools_tests/src/expected/StdlibMigrationNoCompile_Array.res.expected new file mode 100644 index 0000000000..009a006db0 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigrationNoCompile_Array.res.expected @@ -0,0 +1,4 @@ +// Migrations that will not compile after migration (by design) +let sortInPlaceWith1 = [3, 1, 2]->Array.sort((a, b) => a - b) +let sortInPlaceWith2 = Array.sort([3, 1, 2], (a, b) => a - b) + diff --git a/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected index 6115327e82..4f18613d4e 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_Array.res.expected @@ -60,8 +60,8 @@ let lastIndexOfFrom2 = Array.lastIndexOfFrom([1, 2, 1, 3, 1], 1, 3) let copy1 = [1, 2, 3]->Array.copy let copy2 = Array.copy([1, 2, 3]) -let sliceFrom1 = [1, 2, 3, 4]->Array.sliceToEnd(2) -let sliceFrom2 = Array.sliceToEnd([1, 2, 3, 4], 2) +let sliceFrom1 = [1, 2, 3, 4]->Array.sliceToEnd(~start=2) +let sliceFrom2 = Array.sliceToEnd([1, 2, 3, 4], ~start=2) let toString1 = [1, 2, 3]->Array.toString let toString2 = Array.toString([1, 2, 3]) @@ -140,12 +140,30 @@ let sortInPlace2 = Array.toSorted(["c", "a", "b"], (a, b) => %todo("This needs a comparator function. Use `String.compare` for strings, etc.") ) -let sortInPlaceWith1 = [3, 1, 2]->Array.sort((a, b) => a - b) -let sortInPlaceWith2 = Array.sort([3, 1, 2], (a, b) => a - b) - let unshift1 = [1, 2, 3]->Array.unshift(4) let unshift2 = Array.unshift([1, 2, 3], 4) let unshiftMany1 = [1, 2, 3]->Array.unshiftMany([4, 5]) let unshiftMany2 = Array.unshiftMany([1, 2, 3], [4, 5]) +let reduce1 = [1, 2, 3]->Array.reduce(0, (acc, x) => acc + x) +let reduce2 = Array.reduce([1, 2, 3], 0, (acc, x) => acc + x) + +let spliceInPlace1 = [1, 2, 3]->Array.splice(~start=1, ~remove=1, ~insert=[4, 5]) +let spliceInPlace2 = Array.splice([1, 2, 3], ~start=1, ~remove=1, ~insert=[4, 5]) + +let removeFromInPlace1 = [1, 2, 3]->Array.removeInPlace(1) +let removeFromInPlace2 = Array.removeInPlace([1, 2, 3], 1) + +let removeCountInPlace1 = [1, 2, 3]->Array.splice(~start=1, ~remove=1, ~insert=[]) +let removeCountInPlace2 = Array.splice([1, 2, 3], ~start=1, ~remove=1, ~insert=[]) + +let reducei1 = [1, 2, 3]->Array.reduceWithIndex(0, (acc, x, i) => acc + x + i) +let reducei2 = Array.reduceWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) + +let reduceRight1 = [1, 2, 3]->Array.reduceRight(0, (acc, x) => acc + x) +let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) + +let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) +let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) + diff --git a/tests/tools_tests/src/migrate/StdlibMigrationNoCompile_Array.res b/tests/tools_tests/src/migrate/StdlibMigrationNoCompile_Array.res new file mode 100644 index 0000000000..369db964f3 --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigrationNoCompile_Array.res @@ -0,0 +1,3 @@ +// Migrations that will not compile after migration (by design) +let sortInPlaceWith1 = [3, 1, 2]->Js.Array2.sortInPlaceWith((a, b) => a - b) +let sortInPlaceWith2 = Js.Array2.sortInPlaceWith([3, 1, 2], (a, b) => a - b) diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Array.res b/tests/tools_tests/src/migrate/StdlibMigration_Array.res index 4882cb8c91..f69210e705 100644 --- a/tests/tools_tests/src/migrate/StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/StdlibMigration_Array.res @@ -135,11 +135,29 @@ let pushMany2 = Js.Array2.pushMany([1, 2, 3], [4, 5]) let sortInPlace1 = ["c", "a", "b"]->Js.Array2.sortInPlace let sortInPlace2 = Js.Array2.sortInPlace(["c", "a", "b"]) -let sortInPlaceWith1 = [3, 1, 2]->Js.Array2.sortInPlaceWith((a, b) => a - b) -let sortInPlaceWith2 = Js.Array2.sortInPlaceWith([3, 1, 2], (a, b) => a - b) - let unshift1 = [1, 2, 3]->Js.Array2.unshift(4) let unshift2 = Js.Array2.unshift([1, 2, 3], 4) let unshiftMany1 = [1, 2, 3]->Js.Array2.unshiftMany([4, 5]) let unshiftMany2 = Js.Array2.unshiftMany([1, 2, 3], [4, 5]) + +let reduce1 = [1, 2, 3]->Js.Array2.reduce((acc, x) => acc + x, 0) +let reduce2 = Js.Array2.reduce([1, 2, 3], (acc, x) => acc + x, 0) + +let spliceInPlace1 = [1, 2, 3]->Js.Array2.spliceInPlace(~pos=1, ~remove=1, ~add=[4, 5]) +let spliceInPlace2 = Js.Array2.spliceInPlace([1, 2, 3], ~pos=1, ~remove=1, ~add=[4, 5]) + +let removeFromInPlace1 = [1, 2, 3]->Js.Array2.removeFromInPlace(~pos=1) +let removeFromInPlace2 = Js.Array2.removeFromInPlace([1, 2, 3], ~pos=1) + +let removeCountInPlace1 = [1, 2, 3]->Js.Array2.removeCountInPlace(~pos=1, ~count=1) +let removeCountInPlace2 = Js.Array2.removeCountInPlace([1, 2, 3], ~pos=1, ~count=1) + +let reducei1 = [1, 2, 3]->Js.Array2.reducei((acc, x, i) => acc + x + i, 0) +let reducei2 = Js.Array2.reducei([1, 2, 3], (acc, x, i) => acc + x + i, 0) + +let reduceRight1 = [1, 2, 3]->Js.Array2.reduceRight((acc, x) => acc + x, 0) +let reduceRight2 = Js.Array2.reduceRight([1, 2, 3], (acc, x) => acc + x, 0) + +let reduceRighti1 = [1, 2, 3]->Js.Array2.reduceRighti((acc, x, i) => acc + x + i, 0) +let reduceRighti2 = Js.Array2.reduceRighti([1, 2, 3], (acc, x, i) => acc + x + i, 0) \ No newline at end of file diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res new file mode 100644 index 0000000000..4f18613d4e --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -0,0 +1,169 @@ +let shift1 = [1, 2, 3]->Array.shift +let shift2 = Array.shift([1, 2, 3]) + +let slice1 = [1, 2, 3]->Array.slice(~start=1, ~end=2) +let slice2 = Array.slice([1, 2, 3], ~start=1, ~end=2) + +external someArrayLike: Js_array2.array_like = "whatever" + +let from1 = someArrayLike->Array.fromArrayLike +let from2 = Array.fromArrayLike(someArrayLike) + +let fromMap1 = someArrayLike->Array.fromArrayLikeWithMap(s => s ++ "!") +let fromMap2 = Array.fromArrayLikeWithMap(someArrayLike, s => s ++ "!") + +let isArray1 = [1, 2, 3]->Array.isArray +let isArray2 = Array.isArray([1, 2, 3]) + +let length1 = [1, 2, 3]->Array.length +let length2 = Array.length([1, 2, 3]) + +let fillInPlace1 = [1, 2, 3]->Array.fillAll(0) +let fillInPlace2 = Array.fillAll([1, 2, 3], 0) + +let fillFromInPlace1 = [1, 2, 3, 4]->Array.fillToEnd(0, ~start=2) +let fillFromInPlace2 = Array.fillToEnd([1, 2, 3, 4], 0, ~start=2) + +let fillRangeInPlace1 = [1, 2, 3, 4]->Array.fill(0, ~start=1, ~end=3) +let fillRangeInPlace2 = Array.fill([1, 2, 3, 4], 0, ~start=1, ~end=3) + +let pop1 = [1, 2, 3]->Array.pop +let pop2 = Array.pop([1, 2, 3]) + +let reverseInPlace1 = [1, 2, 3]->Array.reverse +let reverseInPlace2 = Array.reverse([1, 2, 3]) + +let concat1 = [1, 2]->Array.concat([3, 4]) +let concat2 = Array.concat([1, 2], [3, 4]) + +let concatMany1 = [1, 2]->Array.concatMany([[3, 4], [5, 6]]) +let concatMany2 = Array.concatMany([1, 2], [[3, 4], [5, 6]]) + +let includes1 = [1, 2, 3]->Array.includes(2) +let includes2 = Array.includes([1, 2, 3], 2) + +let indexOf1 = [1, 2, 3]->Array.indexOf(2) +let indexOf2 = Array.indexOf([1, 2, 3], 2) + +let indexOfFrom1 = [1, 2, 1, 3]->Array.indexOfFrom(1, 2) +let indexOfFrom2 = Array.indexOfFrom([1, 2, 1, 3], 1, 2) + +let joinWith1 = [1, 2, 3]->Array.joinUnsafe(",") +let joinWith2 = Array.joinUnsafe([1, 2, 3], ",") + +let lastIndexOf1 = [1, 2, 1, 3]->Array.lastIndexOf(1) +let lastIndexOf2 = Array.lastIndexOf([1, 2, 1, 3], 1) + +let lastIndexOfFrom1 = [1, 2, 1, 3, 1]->Array.lastIndexOfFrom(1, 3) +let lastIndexOfFrom2 = Array.lastIndexOfFrom([1, 2, 1, 3, 1], 1, 3) + +let copy1 = [1, 2, 3]->Array.copy +let copy2 = Array.copy([1, 2, 3]) + +let sliceFrom1 = [1, 2, 3, 4]->Array.sliceToEnd(~start=2) +let sliceFrom2 = Array.sliceToEnd([1, 2, 3, 4], ~start=2) + +let toString1 = [1, 2, 3]->Array.toString +let toString2 = Array.toString([1, 2, 3]) + +let toLocaleString1 = [1, 2, 3]->Array.toLocaleString +let toLocaleString2 = Array.toLocaleString([1, 2, 3]) + +let every1 = [2, 4, 6]->Array.every(x => mod(x, 2) == 0) +let every2 = Array.every([2, 4, 6], x => mod(x, 2) == 0) + +let everyi1 = [0, 1, 2]->Array.everyWithIndex((x, i) => x == i) +let everyi2 = Array.everyWithIndex([0, 1, 2], (x, i) => x == i) + +let filter1 = [1, 2, 3, 4]->Array.filter(x => x > 2) +let filter2 = Array.filter([1, 2, 3, 4], x => x > 2) + +let filteri1 = [0, 1, 2, 3]->Array.filterWithIndex((_x, i) => i > 1) +let filteri2 = Array.filterWithIndex([0, 1, 2, 3], (_x, i) => i > 1) + +let find1 = [1, 2, 3, 4]->Array.find(x => x > 2) +let find2 = Array.find([1, 2, 3, 4], x => x > 2) + +let findi1 = [0, 1, 2, 3]->Array.findWithIndex((_x, i) => i > 1) +let findi2 = Array.findWithIndex([0, 1, 2, 3], (_x, i) => i > 1) + +let findIndex1 = [1, 2, 3, 4]->Array.findIndex(x => x > 2) +let findIndex2 = Array.findIndex([1, 2, 3, 4], x => x > 2) + +let findIndexi1 = [0, 1, 2, 3]->Array.findIndexWithIndex((_x, i) => i > 1) +let findIndexi2 = Array.findIndexWithIndex([0, 1, 2, 3], (_x, i) => i > 1) + +let forEach1 = [1, 2, 3]->Array.forEach(x => ignore(x)) +let forEach2 = Array.forEach([1, 2, 3], x => ignore(x)) + +let forEachi1 = [1, 2, 3]->Array.forEachWithIndex((x, i) => ignore(x + i)) +let forEachi2 = Array.forEachWithIndex([1, 2, 3], (x, i) => ignore(x + i)) + +let map1 = [1, 2, 3]->Array.map(x => x * 2) +let map2 = Array.map([1, 2, 3], x => x * 2) + +let mapi1 = [1, 2, 3]->Array.mapWithIndex((x, i) => x + i) +let mapi2 = Array.mapWithIndex([1, 2, 3], (x, i) => x + i) + +let some1 = [1, 2, 3, 4]->Array.some(x => x > 3) +let some2 = Array.some([1, 2, 3, 4], x => x > 3) + +let somei1 = [0, 1, 2, 3]->Array.someWithIndex((_x, i) => i > 2) +let somei2 = Array.someWithIndex([0, 1, 2, 3], (_x, i) => i > 2) + +let unsafeGet1 = [1, 2, 3]->Array.getUnsafe(1) +let unsafeGet2 = Array.getUnsafe([1, 2, 3], 1) + +let unsafeSet1 = [1, 2, 3]->Array.setUnsafe(1, 5) +let unsafeSet2 = Array.setUnsafe([1, 2, 3], 1, 5) + +let copyWithin1 = [1, 2, 3, 4, 5]->Array.copyAllWithin(~target=2) +let copyWithin2 = Array.copyAllWithin([1, 2, 3, 4, 5], ~target=2) + +let copyWithinFrom1 = [1, 2, 3, 4, 5]->Array.copyWithinToEnd(~target=0, ~start=2) +let copyWithinFrom2 = Array.copyWithinToEnd([1, 2, 3, 4, 5], ~target=0, ~start=2) + +let copyWithinFromRange1 = [1, 2, 3, 4, 5, 6]->Array.copyWithin(~target=1, ~start=2, ~end=5) +let copyWithinFromRange2 = Array.copyWithin([1, 2, 3, 4, 5, 6], ~target=1, ~start=2, ~end=5) + +let push1 = [1, 2, 3]->Array.push(4) +let push2 = Array.push([1, 2, 3], 4) + +let pushMany1 = [1, 2, 3]->Array.pushMany([4, 5]) +let pushMany2 = Array.pushMany([1, 2, 3], [4, 5]) + +let sortInPlace1 = + ["c", "a", "b"]->Array.toSorted((a, b) => + %todo("This needs a comparator function. Use `String.compare` for strings, etc.") + ) +let sortInPlace2 = Array.toSorted(["c", "a", "b"], (a, b) => + %todo("This needs a comparator function. Use `String.compare` for strings, etc.") +) + +let unshift1 = [1, 2, 3]->Array.unshift(4) +let unshift2 = Array.unshift([1, 2, 3], 4) + +let unshiftMany1 = [1, 2, 3]->Array.unshiftMany([4, 5]) +let unshiftMany2 = Array.unshiftMany([1, 2, 3], [4, 5]) + +let reduce1 = [1, 2, 3]->Array.reduce(0, (acc, x) => acc + x) +let reduce2 = Array.reduce([1, 2, 3], 0, (acc, x) => acc + x) + +let spliceInPlace1 = [1, 2, 3]->Array.splice(~start=1, ~remove=1, ~insert=[4, 5]) +let spliceInPlace2 = Array.splice([1, 2, 3], ~start=1, ~remove=1, ~insert=[4, 5]) + +let removeFromInPlace1 = [1, 2, 3]->Array.removeInPlace(1) +let removeFromInPlace2 = Array.removeInPlace([1, 2, 3], 1) + +let removeCountInPlace1 = [1, 2, 3]->Array.splice(~start=1, ~remove=1, ~insert=[]) +let removeCountInPlace2 = Array.splice([1, 2, 3], ~start=1, ~remove=1, ~insert=[]) + +let reducei1 = [1, 2, 3]->Array.reduceWithIndex(0, (acc, x, i) => acc + x + i) +let reducei2 = Array.reduceWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) + +let reduceRight1 = [1, 2, 3]->Array.reduceRight(0, (acc, x) => acc + x) +let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) + +let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) +let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) + diff --git a/tests/tools_tests/test.sh b/tests/tools_tests/test.sh index 49c2f5b1a4..79cbb36efe 100755 --- a/tests/tools_tests/test.sh +++ b/tests/tools_tests/test.sh @@ -43,11 +43,11 @@ for file in src/migrate/*.{res,resi}; do done # Move migrated files to expected directory so they can be compiled in the project -#for file in src/migrate/StdlibMigration_*.res; do -# expected_file="src/expected/$(basename $file).expected" -# output="src/migrate/migrated/Migrated_$(basename $file)" -# cp -f "$expected_file" "$output" -#done +for file in src/migrate/StdlibMigration_*.res; do + expected_file="src/expected/$(basename $file).expected" + output="src/migrate/migrated/Migrated_$(basename $file)" + cp -f "$expected_file" "$output" +done warningYellow='\033[0;33m' successGreen='\033[0;32m' diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 083aee665e..d98dafc202 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1311,8 +1311,10 @@ module Migrate = struct *) module MapperUtils = struct - let get_template_args_to_insert mapper template_args source_args = + let get_template_args_to_insert ?(is_pipe = false) mapper template_args + source_args = let labelled_args_that_will_be_mapped = ref [] in + let unlabelled_positions_that_will_be_mapped = ref [] in let mapped_template_args = template_args |> List.filter_map (fun (label, arg) -> @@ -1336,7 +1338,7 @@ module Migrate = struct }; ] ); } ) -> ( - (* Map unlabelled args *) + (* Map labelled args *) let arg_in_source_args = source_args |> List.find_map (fun (label, arg) -> @@ -1352,18 +1354,94 @@ module Migrate = struct labelled_args_that_will_be_mapped := arg_name :: !labelled_args_that_will_be_mapped; Some (Asttypes.Nolabel, arg)) + | ( template_label, + { + Parsetree.pexp_desc = + Pexp_extension + ( {txt = "insert.unlabelledArgument"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_constant + (Pconst_integer (count_str, _)); + }, + _ ); + }; + ] ); + } ) -> ( + (* Map unlabelled args *) + let count = int_of_string count_str in + if count <= 0 then None + else + (* Count unlabelled arguments properly: + - For regular calls: count=1 refers to 2nd unlabelled arg (since 1st can't be remapped due to pipes) + - For pipe calls: count=1 refers to 1st unlabelled arg in pipe_args *) + let target_count = if is_pipe then count else count + 1 in + let unlabelled_count = ref 0 in + let found_arg = ref None in + source_args + |> List.iter (fun (label, arg) -> + match label with + | Asttypes.Nolabel -> + incr unlabelled_count; + if !unlabelled_count = target_count then + found_arg := Some arg + | _ -> ()); + match !found_arg with + | None -> None + | Some arg -> + unlabelled_positions_that_will_be_mapped := + target_count :: !unlabelled_positions_that_will_be_mapped; + Some (template_label, arg)) | ( _, { pexp_desc = - Pexp_extension ({txt = "insert.labelledArgument"}, _); + Pexp_extension + ( { + txt = + ( "insert.labelledArgument" + | "insert.unlabelledArgument" ); + }, + _ ); } ) -> - (* Do not add any other instance of insert.labelledArgument. *) + (* Do not add any other instance of insert.* *) None | _, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)} -> None | _ -> Some (label, mapper.Ast_mapper.expr mapper arg)) in - (mapped_template_args, !labelled_args_that_will_be_mapped) + ( mapped_template_args, + !labelled_args_that_will_be_mapped, + !unlabelled_positions_that_will_be_mapped ) + + let filter_and_transform_args source_args ~unlabelled_positions_to_insert + ~will_be_mapped ~labelled_args_map = + let unlabelled_consumed = ref 0 in + source_args + |> List.filter_map (fun (label, arg) -> + match label with + | Asttypes.Nolabel -> + incr unlabelled_consumed; + if List.mem !unlabelled_consumed unlabelled_positions_to_insert + then + (* This unlabelled arg has been mapped already, do not add. *) + None + else Some (label, arg) + | Asttypes.Labelled {loc; txt = label} + | Optional {loc; txt = label} + when StringMap.mem label labelled_args_map -> + (* Map labelled args that has just changed name. *) + let mapped_label_name = StringMap.find label labelled_args_map in + Some (Asttypes.Labelled {loc; txt = mapped_label_name}, arg) + | (Labelled {txt} | Optional {txt}) + when List.mem txt will_be_mapped -> + (* These have been mapped already, do not add. *) + None + | label -> Some (label, arg)) let build_labelled_args_map template_args = template_args @@ -1391,6 +1469,20 @@ module Migrate = struct Some (arg_name, label) | _ -> None) |> StringMap.of_list + + let apply_migration_template ?(is_pipe = false) mapper template_args + source_args = + let labelled_args_map = build_labelled_args_map template_args in + let ( template_args_to_insert, + will_be_mapped, + unlabelled_positions_to_insert ) = + get_template_args_to_insert ~is_pipe mapper template_args source_args + in + let filtered_args = + filter_and_transform_args source_args ~unlabelled_positions_to_insert + ~will_be_mapped ~labelled_args_map + in + filtered_args @ template_args_to_insert end let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = @@ -1450,11 +1542,8 @@ module Migrate = struct transformed_jsx; }; } -> - let labelled_args_map = - MapperUtils.build_labelled_args_map template_args - in - let template_args_to_insert, will_be_mapped = - MapperUtils.get_template_args_to_insert mapper template_args + let migrated_args = + MapperUtils.apply_migration_template mapper template_args source_args in { @@ -1463,27 +1552,7 @@ module Migrate = struct Pexp_apply { funct = template_funct; - args = - (source_args - |> List.filter_map (fun (label, arg) -> - match label with - | Asttypes.Labelled {loc; txt = label} - | Optional {loc; txt = label} - when StringMap.mem label labelled_args_map -> - (* Map labelled args that has just changed name. *) - let mapped_label_name = - StringMap.find label labelled_args_map - in - Some - ( Asttypes.Labelled - {loc; txt = mapped_label_name}, - arg ) - | (Labelled {txt} | Optional {txt}) - when List.mem txt will_be_mapped -> - (* These have been mapped already, do not add. *) - None - | label -> Some (label, arg))) - @ template_args_to_insert; + args = migrated_args; partial; transformed_jsx; }; @@ -1523,7 +1592,7 @@ module Migrate = struct transformed_jsx; }; } -> - let template_args_to_insert, _ = + let template_args_to_insert, _, _ = MapperUtils.get_template_args_to_insert mapper template_args [] in @@ -1561,6 +1630,66 @@ module Migrate = struct (* TODO: More elaborate warnings etc *) (* Invalid config. *) exp) + | { + pexp_desc = + Pexp_apply + { + funct = {pexp_desc = Pexp_ident {txt = Lident "->"}} as funct; + args = + (_ as lhs) + :: ( Nolabel, + { + pexp_desc = + Pexp_apply + {funct = {pexp_loc = fn_loc}; args = pipe_args}; + } ) + :: _; + }; + } + when Hashtbl.mem loc_to_deprecated_fn_call fn_loc -> ( + (* Pipe with arguments, [1, 2, 3, 4]->someDeprecated(arg1, arg2) *) + let deprecated_info = + Hashtbl.find loc_to_deprecated_fn_call fn_loc + in + Hashtbl.remove loc_to_deprecated_fn_call fn_loc; + + match deprecated_info.migration_template with + | Some + { + pexp_desc = + Pexp_apply + { + funct = template_funct; + args = template_args; + partial; + transformed_jsx; + }; + } -> + let migrated_args = + MapperUtils.apply_migration_template ~is_pipe:true mapper + template_args pipe_args + in + { + exp with + pexp_desc = + Pexp_apply + { + funct; + args = + [ + lhs; + ( Nolabel, + Ast_helper.Exp.apply template_funct migrated_args + ); + ]; + partial; + transformed_jsx; + }; + } + | _ -> + (* TODO: More elaborate warnings etc *) + (* Invalid config. *) + exp) | _ -> Ast_mapper.default_mapper.expr mapper exp); } in From 384b82facc24e61ab3672f6c73be1511060a6e0c Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:12:15 +0200 Subject: [PATCH 10/43] format code --- runtime/Js_array2.res | 154 ++++++++++-------- .../src/migrate/StdlibMigration_Array.res | 16 +- 2 files changed, 101 insertions(+), 69 deletions(-) diff --git a/runtime/Js_array2.res b/runtime/Js_array2.res index e716e3075d..79d89a950a 100644 --- a/runtime/Js_array2.res +++ b/runtime/Js_array2.res @@ -79,7 +79,7 @@ Js.Array2.from(strArr) == ["a", "b", "c", "d"] */ @deprecated({ reason: "Use `Array.fromArrayLike` instead.", - migrate: Array.fromArrayLike() + migrate: Array.fromArrayLike(), }) @val external from: array_like<'a> => array<'a> = "Array.from" @@ -102,7 +102,7 @@ Js.Array2.fromMap(strArr, code) == [97.0, 98.0, 99.0, 100.0] */ @deprecated({ reason: "Use `Array.fromArrayLikeWithMap` instead.", - migrate: Array.fromArrayLikeWithMap() + migrate: Array.fromArrayLikeWithMap(), }) @val external fromMap: (array_like<'a>, 'a => 'b) => array<'b> = "Array.from" @@ -122,7 +122,7 @@ Js.Array2.isArray("abcd") == false */ @deprecated({ reason: "Use `Array.isArray` instead.", - migrate: Array.isArray() + migrate: Array.isArray(), }) @val external isArray: 'a => bool = "Array.isArray" @@ -134,7 +134,7 @@ on MDN. */ @deprecated({ reason: "Use `Array.length` instead.", - migrate: Array.length() + migrate: Array.length(), }) @get external length: array<'a> => int = "length" @@ -158,7 +158,7 @@ arr == [100, 101, 100, 101, 102] */ @deprecated({ reason: "Use `Array.copyAllWithin` instead.", - migrate: Array.copyAllWithin(~target=%insert.labelledArgument("to_")) + migrate: Array.copyAllWithin(~target=%insert.labelledArgument("to_")), }) @send external copyWithin: (t<'a>, ~to_: int) => t<'a> = "copyWithin" @@ -182,7 +182,10 @@ arr == [102, 103, 104, 103, 104] */ @deprecated({ reason: "Use `Array.copyWithinToEnd` instead.", - migrate: Array.copyWithinToEnd(~target=%insert.labelledArgument("to_"), ~start=%insert.labelledArgument("from")) + migrate: Array.copyWithinToEnd( + ~target=%insert.labelledArgument("to_"), + ~start=%insert.labelledArgument("from"), + ), }) @send external copyWithinFrom: (t<'a>, ~to_: int, ~from: int) => t<'a> = "copyWithin" @@ -206,7 +209,10 @@ arr == [100, 102, 103, 104, 104, 105] */ @deprecated({ reason: "Use `Array.copyWithin` instead.", - migrate: Array.copyWithin(~target=%insert.labelledArgument("to_"), ~end=%insert.labelledArgument("end_")) + migrate: Array.copyWithin( + ~target=%insert.labelledArgument("to_"), + ~end=%insert.labelledArgument("end_"), + ), }) @send external copyWithinFromRange: (t<'a>, ~to_: int, ~start: int, ~end_: int) => t<'a> = "copyWithin" @@ -232,7 +238,7 @@ arr == [99, 99, 99, 99, 99] */ @deprecated({ reason: "Use `Array.fillAll` instead.", - migrate: Array.fillAll() + migrate: Array.fillAll(), }) @send external fillInPlace: (t<'a>, 'a) => t<'a> = "fill" @@ -256,7 +262,7 @@ arr == [100, 101, 99, 99, 99] */ @deprecated({ reason: "Use `Array.fillToEnd` instead.", - migrate: Array.fillToEnd(~start=%insert.labelledArgument("from")) + migrate: Array.fillToEnd(~start=%insert.labelledArgument("from")), }) @send external fillFromInPlace: (t<'a>, 'a, ~from: int) => t<'a> = "fill" @@ -281,7 +287,7 @@ arr == [100, 99, 99, 99, 104] */ @deprecated({ reason: "Use `Array.fill` instead.", - migrate: Array.fill(~end=%insert.labelledArgument("end_")) + migrate: Array.fill(~end=%insert.labelledArgument("end_")), }) @send external fillRangeInPlace: (t<'a>, 'a, ~start: int, ~end_: int) => t<'a> = "fill" @@ -308,7 +314,7 @@ Js.Array2.pop(empty) == None */ @deprecated({ reason: "Use `Array.pop` instead.", - migrate: Array.pop() + migrate: Array.pop(), }) @send external pop: t<'a> => option<'a> = "pop" @@ -329,7 +335,7 @@ arr == ["ant", "bee", "cat", "dog"] */ @deprecated({ reason: "Use `Array.push` instead. Note: `Array.push` returns `unit`, not the array length.", - migrate: Array.push() + migrate: Array.push(), }) @send external push: (t<'a>, 'a) => int = "push" @@ -351,9 +357,10 @@ arr == ["ant", "bee", "cat", "dog", "elk"] */ @deprecated({ reason: "Use `Array.pushMany` instead. Note: `Array.pushMany` returns `unit`, not the array length.", - migrate: Array.pushMany() + migrate: Array.pushMany(), }) -@send @variadic +@send +@variadic external pushMany: (t<'a>, array<'a>) => int = "push" /** @@ -372,7 +379,7 @@ arr == ["cat", "bee", "ant"] */ @deprecated({ reason: "Use `Array.reverse` instead.", - migrate: Array.reverse() + migrate: Array.reverse(), }) @send external reverseInPlace: t<'a> => t<'a> = "reverse" @@ -397,7 +404,7 @@ Js.Array2.shift(empty) == None */ @deprecated({ reason: "Use `Array.shift` instead.", - migrate: Array.shift() + migrate: Array.shift(), }) @send external shift: t<'a> => option<'a> = "shift" @@ -424,7 +431,9 @@ numbers == [1, 10, 2, 20, 3, 30] */ @deprecated({ reason: "Use `Array.toSorted` instead.", - migrate: Array.toSorted((a, b) => %todo_("This needs a comparator function. Use `String.compare` for strings, etc.")) + migrate: Array.toSorted((a, b) => + %todo_("This needs a comparator function. Use `String.compare` for strings, etc.") + ), }) @send external sortInPlace: t<'a> => t<'a> = "sort" @@ -461,7 +470,7 @@ Js.Array2.sortInPlaceWith(numbers, reverseNumeric) == [30, 20, 10, 3, 2, 1] */ @deprecated({ reason: "Use `Array.sort` instead.", - migrate: Array.sort() + migrate: Array.sort(), }) @send external sortInPlaceWith: (t<'a>, ('a, 'a) => int) => t<'a> = "sort" @@ -489,9 +498,15 @@ Js.Array2.spliceInPlace(arr3, ~pos=9, ~remove=2, ~add=["x", "y", "z"]) == [] arr3 == ["a", "b", "c", "d", "e", "f", "x", "y", "z"] ``` */ -@send @variadic @deprecated({ +@send +@variadic +@deprecated({ reason: "Use `Array.splice` instead.", - migrate: Array.splice(~start=%insert.labelledArgument("pos"), ~remove=%insert.labelledArgument("remove"), ~insert=%insert.labelledArgument("add")) + migrate: Array.splice( + ~start=%insert.labelledArgument("pos"), + ~remove=%insert.labelledArgument("remove"), + ~insert=%insert.labelledArgument("add"), + ), }) external spliceInPlace: (t<'a>, ~pos: int, ~remove: int, ~add: array<'a>) => t<'a> = "splice" @@ -510,9 +525,10 @@ Js.Array2.removeFromInPlace(arr, ~pos=4) == ["e", "f"] arr == ["a", "b", "c", "d"] ``` */ -@send @deprecated({ +@send +@deprecated({ reason: "Use `Array.removeInPlace` instead.", - migrate: Array.removeInPlace(%insert.labelledArgument("pos")) + migrate: Array.removeInPlace(%insert.labelledArgument("pos")), }) external removeFromInPlace: (t<'a>, ~pos: int) => t<'a> = "splice" @@ -531,9 +547,14 @@ Js.Array2.removeCountInPlace(arr, ~pos=2, ~count=3) == ["c", "d", "e"] arr == ["a", "b", "f"] ``` */ -@send @deprecated({ +@send +@deprecated({ reason: "Use `Array.splice` instead.", - migrate: Array.splice(~start=%insert.labelledArgument("pos"), ~remove=%insert.labelledArgument("count"), ~insert=[]) + migrate: Array.splice( + ~start=%insert.labelledArgument("pos"), + ~remove=%insert.labelledArgument("count"), + ~insert=[], + ), }) external removeCountInPlace: (t<'a>, ~pos: int, ~count: int) => t<'a> = "splice" @@ -553,7 +574,7 @@ arr == ["a", "b", "c", "d"] */ @deprecated({ reason: "Use `Array.unshift` instead.", - migrate: Array.unshift() + migrate: Array.unshift(), }) @send external unshift: (t<'a>, 'a) => int = "unshift" @@ -575,9 +596,10 @@ arr == ["a", "b", "c", "d", "e"] */ @deprecated({ reason: "Use `Array.unshiftMany` instead.", - migrate: Array.unshiftMany() + migrate: Array.unshiftMany(), }) -@send @variadic +@send +@variadic external unshiftMany: (t<'a>, array<'a>) => int = "unshift" /* Accessor functions @@ -599,7 +621,7 @@ Js.Array2.concat(["a", "b"], ["c", "d", "e"]) == ["a", "b", "c", "d", "e"] */ @deprecated({ reason: "Use `Array.concat` instead.", - migrate: Array.concat() + migrate: Array.concat(), }) @send external concat: (t<'a>, t<'a>) => t<'a> = "concat" @@ -627,9 +649,10 @@ Js.Array2.concatMany(["a", "b", "c"], [["d", "e"], ["f", "g", "h"]]) == [ */ @deprecated({ reason: "Use `Array.concatMany` instead.", - migrate: Array.concatMany() + migrate: Array.concatMany(), }) -@send @variadic +@send +@variadic external concatMany: (t<'a>, array>) => t<'a> = "concat" /** @@ -646,7 +669,7 @@ Js.Array2.includes(["a", "b", "c"], "x") == false */ @deprecated({ reason: "Use `Array.includes` instead.", - migrate: Array.includes() + migrate: Array.includes(), }) @send external includes: (t<'a>, 'a) => bool = "includes" @@ -666,7 +689,7 @@ Js.Array2.indexOf([100, 101, 102, 103], 999) == -1 */ @deprecated({ reason: "Use `Array.indexOf` instead.", - migrate: Array.indexOf() + migrate: Array.indexOf(), }) @send external indexOf: (t<'a>, 'a) => int = "indexOf" @@ -687,7 +710,7 @@ Js.Array2.indexOfFrom(["a", "b", "a", "c", "a"], "b", ~from=2) == -1 */ @deprecated({ reason: "Use `Array.indexOfFrom` instead.", - migrate: Array.indexOfFrom(%insert.labelledArgument("from")) + migrate: Array.indexOfFrom(%insert.labelledArgument("from")), }) @send external indexOfFrom: (t<'a>, 'a, ~from: int) => int = "indexOf" @@ -710,7 +733,7 @@ Js.Array2.joinWith([2.5, 3.6, 3e-2], ";") == "2.5;3.6;0.03" */ @deprecated({ reason: "Use `Array.joinUnsafe` instead.", - migrate: Array.joinUnsafe() + migrate: Array.joinUnsafe(), }) @send external joinWith: (t<'a>, string) => string = "join" @@ -730,7 +753,7 @@ Js.Array2.lastIndexOf(["a", "b", "a", "c"], "x") == -1 */ @deprecated({ reason: "Use `Array.lastIndexOf` instead.", - migrate: Array.lastIndexOf() + migrate: Array.lastIndexOf(), }) @send external lastIndexOf: (t<'a>, 'a) => int = "lastIndexOf" @@ -751,7 +774,7 @@ Js.Array2.lastIndexOfFrom(["a", "b", "a", "c", "a", "d"], "c", ~from=2) == -1 */ @deprecated({ reason: "Use `Array.lastIndexOfFrom` instead.", - migrate: Array.lastIndexOfFrom(%insert.labelledArgument("from")) + migrate: Array.lastIndexOfFrom(%insert.labelledArgument("from")), }) @send external lastIndexOfFrom: (t<'a>, 'a, ~from: int) => int = "lastIndexOf" @@ -774,7 +797,7 @@ Js.Array2.slice(arr, ~start=9, ~end_=10) == [] */ @deprecated({ reason: "Use `Array.slice` instead.", - migrate: Array.slice(~end=%insert.labelledArgument("end_")) + migrate: Array.slice(~end=%insert.labelledArgument("end_")), }) @send external slice: (t<'a>, ~start: int, ~end_: int) => t<'a> = "slice" @@ -787,7 +810,7 @@ on MDN. */ @deprecated({ reason: "Use `Array.copy` instead.", - migrate: Array.copy() + migrate: Array.copy(), }) @send external copy: t<'a> => t<'a> = "slice" @@ -799,7 +822,7 @@ on MDN. */ @deprecated({ reason: "Use `Array.sliceToEnd` instead.", - migrate: Array.sliceToEnd(~start=%insert.unlabelledArgument(1)) + migrate: Array.sliceToEnd(~start=%insert.unlabelledArgument(1)), }) @send external sliceFrom: (t<'a>, int) => t<'a> = "slice" @@ -820,7 +843,7 @@ Js.Array2.toString(["a", "b", "c"]) == "a,b,c" */ @deprecated({ reason: "Use `Array.toString` instead.", - migrate: Array.toString() + migrate: Array.toString(), }) @send external toString: t<'a> => string = "toString" @@ -843,7 +866,7 @@ Js.Array2.toLocaleString([Js.Date.make()]) */ @deprecated({ reason: "Use `Array.toLocaleString` instead.", - migrate: Array.toLocaleString() + migrate: Array.toLocaleString(), }) @send external toLocaleString: t<'a> => string = "toLocaleString" @@ -872,7 +895,7 @@ Js.Array2.every([6, 22, 7, 4], isEven) == false */ @deprecated({ reason: "Use `Array.every` instead.", - migrate: Array.every() + migrate: Array.every(), }) @send external every: (t<'a>, 'a => bool) => bool = "every" @@ -898,7 +921,7 @@ Js.Array2.everyi([6, 3, -5, 8], evenIndexPositive) == false */ @deprecated({ reason: "Use `Array.everyWithIndex` instead.", - migrate: Array.everyWithIndex() + migrate: Array.everyWithIndex(), }) @send external everyi: (t<'a>, ('a, int) => bool) => bool = "every" @@ -919,7 +942,7 @@ Js.Array2.filter(["abc", "", "", "def", "ghi"], nonEmpty) == ["abc", "def", "ghi */ @deprecated({ reason: "Use `Array.filter` instead.", - migrate: Array.filter() + migrate: Array.filter(), }) @send external filter: (t<'a>, 'a => bool) => t<'a> = "filter" @@ -944,7 +967,7 @@ Js.Array2.filteri([6, 3, 5, 8, 7, -4, 1], positiveOddElement) == [3, 8] */ @deprecated({ reason: "Use `Array.filterWithIndex` instead.", - migrate: Array.filterWithIndex() + migrate: Array.filterWithIndex(), }) @send external filteri: (t<'a>, ('a, int) => bool) => t<'a> = "filter" @@ -965,7 +988,7 @@ Js.Array2.find([33, 22, 55, 77, 44], x => x < 0) == None */ @deprecated({ reason: "Use `Array.find` instead.", - migrate: Array.find() + migrate: Array.find(), }) @send external find: (t<'a>, 'a => bool) => option<'a> = "find" @@ -991,7 +1014,7 @@ Js.Array2.findi([66, -33, 55, -88, 22], positiveOddElement) == None */ @deprecated({ reason: "Use `Array.findWithIndex` instead.", - migrate: Array.findWithIndex() + migrate: Array.findWithIndex(), }) @send external findi: (t<'a>, ('a, int) => bool) => option<'a> = "find" @@ -1013,7 +1036,7 @@ Js.Array2.findIndex([33, 22, 55, 77, 44], x => x < 0) == -1 */ @deprecated({ reason: "Use `Array.findIndex` instead.", - migrate: Array.findIndex() + migrate: Array.findIndex(), }) @send external findIndex: (t<'a>, 'a => bool) => int = "findIndex" @@ -1039,7 +1062,7 @@ Js.Array2.findIndexi([66, -33, 55, -88, 22], positiveOddElement) == -1 */ @deprecated({ reason: "Use `Array.findIndexWithIndex` instead.", - migrate: Array.findIndexWithIndex() + migrate: Array.findIndexWithIndex(), }) @send external findIndexi: (t<'a>, ('a, int) => bool) => int = "findIndex" @@ -1064,7 +1087,7 @@ Js.Array2.forEach(["a", "b", "c"], x => Js.log(x)) == () */ @deprecated({ reason: "Use `Array.forEach` instead.", - migrate: Array.forEach() + migrate: Array.forEach(), }) @send external forEach: (t<'a>, 'a => unit) => unit = "forEach" @@ -1088,7 +1111,7 @@ Js.Array2.forEachi(["a", "b", "c"], (item, index) => Js.log2(index + 1, item)) = */ @deprecated({ reason: "Use `Array.forEachWithIndex` instead.", - migrate: Array.forEachWithIndex() + migrate: Array.forEachWithIndex(), }) @send external forEachi: (t<'a>, ('a, int) => unit) => unit = "forEach" @@ -1113,7 +1136,7 @@ Js.Array2.map(["animal", "vegetable", "mineral"], Js.String.length) == [6, 9, 7] */ @deprecated({ reason: "Use `Array.map` instead.", - migrate: Array.map() + migrate: Array.map(), }) @send external map: (t<'a>, 'a => 'b) => t<'b> = "map" @@ -1136,7 +1159,7 @@ Js.Array2.mapi([10, 11, 12], product) == [0, 11, 24] */ @deprecated({ reason: "Use `Array.mapWithIndex` instead.", - migrate: Array.mapWithIndex() + migrate: Array.mapWithIndex(), }) @send external mapi: (t<'a>, ('a, int) => 'b) => t<'b> = "map" @@ -1174,7 +1197,7 @@ Js.Array2.reduce([2.0, 4.0], (acc, item) => item /. acc, 1.0) == 2.0 // 4.0 / (2 */ @deprecated({ reason: "Use `Array.reduce` instead.", - migrate: Array.reduce(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) + migrate: Array.reduce(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)), }) @send external reduce: (t<'a>, ('b, 'a) => 'b, 'b) => 'b = "reduce" @@ -1211,9 +1234,10 @@ let sumOfEvens = (accumulator, item, index) => Js.Array2.reducei([2, 5, 1, 4, 3], sumOfEvens, 0) == 6 ``` */ -@send @deprecated({ +@send +@deprecated({ reason: "Use `Array.reduceWithIndex` instead.", - migrate: Array.reduceWithIndex(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) + migrate: Array.reduceWithIndex(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)), }) external reducei: (t<'a>, ('b, 'a, int) => 'b, 'b) => 'b = "reduce" @@ -1246,9 +1270,10 @@ Js.Array2.reduceRight([10, 2, 4], sumOfSquares, 0) == 120 Js.Array2.reduceRight([2.0, 4.0], (acc, item) => item /. acc, 1.0) == 0.5 // 2.0 / (4.0 / 1.0) ``` */ -@send @deprecated({ +@send +@deprecated({ reason: "Use `Array.reduceRight` instead.", - migrate: Array.reduceRight(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) + migrate: Array.reduceRight(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)), }) external reduceRight: (t<'a>, ('b, 'a) => 'b, 'b) => 'b = "reduceRight" @@ -1286,9 +1311,10 @@ let sumOfEvens = (accumulator, item, index) => Js.Array2.reduceRighti([2, 5, 1, 4, 3], sumOfEvens, 0) == 6 ``` */ -@send @deprecated({ +@send +@deprecated({ reason: "Use `Array.reduceRightWithIndex` instead.", - migrate: Array.reduceRightWithIndex(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)) + migrate: Array.reduceRightWithIndex(%insert.unlabelledArgument(2), %insert.unlabelledArgument(1)), }) external reduceRighti: (t<'a>, ('b, 'a, int) => 'b, 'b) => 'b = "reduceRight" @@ -1307,7 +1333,7 @@ Js.Array2.some([3, 7, 5, 1, 9], isEven) == false */ @deprecated({ reason: "Use `Array.some` instead.", - migrate: Array.some() + migrate: Array.some(), }) @send external some: (t<'a>, 'a => bool) => bool = "some" @@ -1334,7 +1360,7 @@ Js.Array2.somei(["a", "bc", "def", "gh"], sameLength) == false */ @deprecated({ reason: "Use `Array.someWithIndex` instead.", - migrate: Array.someWithIndex() + migrate: Array.someWithIndex(), }) @send external somei: (t<'a>, ('a, int) => bool) => bool = "some" @@ -1357,7 +1383,7 @@ Js.Array2.unsafe_get(arr, 4) // returns undefined */ @deprecated({ reason: "Use `Array.getUnsafe` instead.", - migrate: Array.getUnsafe() + migrate: Array.getUnsafe(), }) external unsafe_get: (array<'a>, int) => 'a = "%array_unsafe_get" @@ -1386,6 +1412,6 @@ Js.Array2.unsafe_set(arr, -1, 66) */ @deprecated({ reason: "Use `Array.setUnsafe` instead.", - migrate: Array.setUnsafe() + migrate: Array.setUnsafe(), }) external unsafe_set: (array<'a>, int, 'a) => unit = "%array_unsafe_set" diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Array.res b/tests/tools_tests/src/migrate/StdlibMigration_Array.res index f69210e705..cf8e65a744 100644 --- a/tests/tools_tests/src/migrate/StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/StdlibMigration_Array.res @@ -1,8 +1,8 @@ let shift1 = [1, 2, 3]->Js.Array2.shift let shift2 = Js.Array2.shift([1, 2, 3]) -let slice1 = [1,2,3]->Js.Array2.slice(~start=1, ~end_=2) -let slice2 = Js.Array2.slice([1,2,3], ~start=1, ~end_=2) +let slice1 = [1, 2, 3]->Js.Array2.slice(~start=1, ~end_=2) +let slice2 = Js.Array2.slice([1, 2, 3], ~start=1, ~end_=2) external someArrayLike: Js_array2.array_like = "whatever" @@ -123,8 +123,14 @@ let copyWithin2 = Js.Array2.copyWithin([1, 2, 3, 4, 5], ~to_=2) let copyWithinFrom1 = [1, 2, 3, 4, 5]->Js.Array2.copyWithinFrom(~to_=0, ~from=2) let copyWithinFrom2 = Js.Array2.copyWithinFrom([1, 2, 3, 4, 5], ~to_=0, ~from=2) -let copyWithinFromRange1 = [1, 2, 3, 4, 5, 6]->Js.Array2.copyWithinFromRange(~to_=1, ~start=2, ~end_=5) -let copyWithinFromRange2 = Js.Array2.copyWithinFromRange([1, 2, 3, 4, 5, 6], ~to_=1, ~start=2, ~end_=5) +let copyWithinFromRange1 = + [1, 2, 3, 4, 5, 6]->Js.Array2.copyWithinFromRange(~to_=1, ~start=2, ~end_=5) +let copyWithinFromRange2 = Js.Array2.copyWithinFromRange( + [1, 2, 3, 4, 5, 6], + ~to_=1, + ~start=2, + ~end_=5, +) let push1 = [1, 2, 3]->Js.Array2.push(4) let push2 = Js.Array2.push([1, 2, 3], 4) @@ -160,4 +166,4 @@ let reduceRight1 = [1, 2, 3]->Js.Array2.reduceRight((acc, x) => acc + x, 0) let reduceRight2 = Js.Array2.reduceRight([1, 2, 3], (acc, x) => acc + x, 0) let reduceRighti1 = [1, 2, 3]->Js.Array2.reduceRighti((acc, x, i) => acc + x + i, 0) -let reduceRighti2 = Js.Array2.reduceRighti([1, 2, 3], (acc, x, i) => acc + x + i, 0) \ No newline at end of file +let reduceRighti2 = Js.Array2.reduceRighti([1, 2, 3], (acc, x, i) => acc + x + i, 0) From c198c584dac4c3036baaf2353b21e450cf6b4d93 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:17:01 +0200 Subject: [PATCH 11/43] format --- .../src/migrate/migrated/Migrated_StdlibMigration_Array.res | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res index 4f18613d4e..d398c936d3 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -166,4 +166,3 @@ let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) - From 0b7d4735a98e586b70da5e05fba4a5415b3e0a75 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:25:53 +0200 Subject: [PATCH 12/43] update snapshots --- ...-dev-dependency-used-by-non-dev-source.txt | 59 ++++++++++++++++++- rewatch/tests/snapshots/dependency-cycle.txt | 59 ++++++++++++++++++- rewatch/tests/snapshots/remove-file.txt | 59 ++++++++++++++++++- .../rename-file-internal-dep-namespace.txt | 59 ++++++++++++++++++- .../snapshots/rename-file-internal-dep.txt | 59 ++++++++++++++++++- .../snapshots/rename-file-with-interface.txt | 59 ++++++++++++++++++- rewatch/tests/snapshots/rename-file.txt | 59 ++++++++++++++++++- .../tests/snapshots/rename-interface-file.txt | 57 +++++++++++++++++- 8 files changed, 447 insertions(+), 23 deletions(-) diff --git a/rewatch/tests/snapshots/bs-dev-dependency-used-by-non-dev-source.txt b/rewatch/tests/snapshots/bs-dev-dependency-used-by-non-dev-source.txt index 34ddd13310..d5263d3781 100644 --- a/rewatch/tests/snapshots/bs-dev-dependency-used-by-non-dev-source.txt +++ b/rewatch/tests/snapshots/bs-dev-dependency-used-by-non-dev-source.txt @@ -1,6 +1,59 @@ -Cleaned 0/16 -Parsed 2 source files -Compiled 2 modules +Cleaned 0/15 +Parsed 3 source files +Compiled 3 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/dependency-cycle.txt b/rewatch/tests/snapshots/dependency-cycle.txt index b0463dddf6..99e4c0e85a 100644 --- a/rewatch/tests/snapshots/dependency-cycle.txt +++ b/rewatch/tests/snapshots/dependency-cycle.txt @@ -1,6 +1,59 @@ -Cleaned 0/16 -Parsed 1 source files -Compiled 0 modules +Cleaned 0/15 +Parsed 2 source files +Compiled 1 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/remove-file.txt b/rewatch/tests/snapshots/remove-file.txt index a1e2468b7f..c914b524cb 100644 --- a/rewatch/tests/snapshots/remove-file.txt +++ b/rewatch/tests/snapshots/remove-file.txt @@ -1,6 +1,59 @@ -Cleaned 1/16 -Parsed 0 source files -Compiled 1 modules +Cleaned 1/15 +Parsed 1 source files +Compiled 2 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt b/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt index 55eb9df4b5..52839e6eab 100644 --- a/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt +++ b/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt @@ -1,6 +1,59 @@ -Cleaned 2/16 -Parsed 2 source files -Compiled 3 modules +Cleaned 2/15 +Parsed 3 source files +Compiled 4 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/rename-file-internal-dep.txt b/rewatch/tests/snapshots/rename-file-internal-dep.txt index 6596297776..a2b51f1a59 100644 --- a/rewatch/tests/snapshots/rename-file-internal-dep.txt +++ b/rewatch/tests/snapshots/rename-file-internal-dep.txt @@ -1,6 +1,59 @@ -Cleaned 2/16 -Parsed 2 source files -Compiled 2 modules +Cleaned 2/15 +Parsed 3 source files +Compiled 4 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/rename-file-with-interface.txt b/rewatch/tests/snapshots/rename-file-with-interface.txt index 4c2522e129..6ed7e51f13 100644 --- a/rewatch/tests/snapshots/rename-file-with-interface.txt +++ b/rewatch/tests/snapshots/rename-file-with-interface.txt @@ -1,7 +1,60 @@  No implementation file found for interface file (skipping): src/ModuleWithInterface.resi -Cleaned 2/16 -Parsed 1 source files -Compiled 2 modules +Cleaned 2/15 +Parsed 2 source files +Compiled 3 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/rename-file.txt b/rewatch/tests/snapshots/rename-file.txt index 29c1b2b461..bc8b44cd8f 100644 --- a/rewatch/tests/snapshots/rename-file.txt +++ b/rewatch/tests/snapshots/rename-file.txt @@ -1,6 +1,59 @@ -Cleaned 1/16 -Parsed 1 source files -Compiled 1 modules +Cleaned 1/15 +Parsed 2 source files +Compiled 2 modules + + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. diff --git a/rewatch/tests/snapshots/rename-interface-file.txt b/rewatch/tests/snapshots/rename-interface-file.txt index 6eabc087b8..d9edb8ca5d 100644 --- a/rewatch/tests/snapshots/rename-interface-file.txt +++ b/rewatch/tests/snapshots/rename-interface-file.txt @@ -1,8 +1,61 @@  No implementation file found for interface file (skipping): src/ModuleWithInterface2.resi -Cleaned 1/16 -Parsed 1 source files +Cleaned 1/15 +Parsed 2 source files Compiled 2 modules + Warning number 3 + /packages/dep02/src/Array.res:6:16-33 + + 4 │ let at = get + 5 │ + 6 │ let includes = Js.Array2.includes + 7 │ + 8 │ let head = t => t->get(0) + + deprecated: Js.Array2.includes + Use `Array.includes` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:24:12-25 + + 22 │ let flatMap = (t, fn) => t->map(fn)->concatMany + 23 │ + 24 │ let mapi = Js.Array2.mapi + 25 │ + 26 │ let flatten = t => t->flatMap(x => x) + + deprecated: Js.Array2.mapi + Use `Array.mapWithIndex` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:32:14-29 + + 30 │ let findIndex = (t, fn) => Js.Array.findIndex(fn, t) + 31 │ + 32 │ let filter = Js.Array2.filter + 33 │ + 34 │ let reject = (t, fn) => t->filter(el => !fn(el)) + + deprecated: Js.Array2.filter + Use `Array.filter` instead. + + + Warning number 3 + /packages/dep02/src/Array.res:48:18-35 + + 46 │ + 47 │ module String = { + 48 │ let joinWith = Js.Array2.joinWith + 49 │ let join = joinWith(_, "") + 50 │ } + + deprecated: Js.Array2.joinWith + Use `Array.joinUnsafe` instead. + + + The field 'bs-dependencies' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version. Use 'dependencies' instead. From c9a5fa1993bf8ba03ee020040166dd32d994cbf5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:31:04 +0200 Subject: [PATCH 13/43] update analysis tests --- tests/analysis_tests/tests/src/expected/Completion.res.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/analysis_tests/tests/src/expected/Completion.res.txt b/tests/analysis_tests/tests/src/expected/Completion.res.txt index 0663346ce6..c0b1160f66 100644 --- a/tests/analysis_tests/tests/src/expected/Completion.res.txt +++ b/tests/analysis_tests/tests/src/expected/Completion.res.txt @@ -285,7 +285,7 @@ Path Array. "kind": 12, "tags": [], "detail": "(\n array<'a>,\n ~target: int,\n ~start: int,\n ~end: int,\n) => array<'a>", - "documentation": null + "documentation": {"kind": "markdown", "value": "\n`copyWithin(array, ~target, ~start, ~end)` copies starting at element `start` in the given array up to but not including `end` to the designated `target` position, returning the resulting array.\n\nBeware this will *mutate* the array.\n\nSee [`Array.copyWithin`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) on MDN.\n\n## Examples\n\n```rescript\nlet arr = [100, 101, 102, 103, 104, 105]\narr->Array.copyWithin(~target=1, ~start=2, ~end=5) == [100, 102, 103, 104, 104, 105]\narr == [100, 102, 103, 104, 104, 105]\n```\n"} }, { "label": "toString", "kind": 12, @@ -477,7 +477,7 @@ Path Array. "kind": 12, "tags": [], "detail": "(array<'a>, ~target: int, ~start: int) => array<'a>", - "documentation": null + "documentation": {"kind": "markdown", "value": "\n`copyWithinToEnd(array, ~target, ~start)` copies starting at element `start` in the given array to the designated `target` position, returning the resulting array.\n\nBeware this will *mutate* the array.\n\nSee [`Array.copyWithin`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) on MDN.\n\n## Examples\n\n```rescript\nlet arr = [100, 101, 102, 103, 104]\narr->Array.copyWithinToEnd(~target=0, ~start=2) == [102, 103, 104, 103, 104]\narr == [102, 103, 104, 103, 104]\n```\n"} }, { "label": "unshift", "kind": 12, @@ -525,7 +525,7 @@ Path Array. "kind": 12, "tags": [], "detail": "(array<'a>, ~target: int) => array<'a>", - "documentation": null + "documentation": {"kind": "markdown", "value": "\n`copyAllWithin(array, ~target)` copies from the first element in the given array to the designated `target` position, returning the resulting array.\n\nBeware this will *mutate* the array.\n\nSee [`Array.copyWithin`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) on MDN.\n\n## Examples\n\n```rescript\nlet arr = [100, 101, 102, 103, 104]\narr->Array.copyAllWithin(~target=2) == [100, 101, 100, 101, 102]\narr == [100, 101, 100, 101, 102]\n```\n"} }, { "label": "keepSome", "kind": 12, From ae58d461cd2123570da32670ec65867f7032ebfb Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:42:10 +0200 Subject: [PATCH 14/43] support value references --- compiler/ml/cmt_utils.ml | 2 +- compiler/ml/typecore.ml | 7 ++++- .../src/expected/FileToMigrate.res.expected | 2 ++ .../src/migrate/DeprecatedStuff.res | 6 +++++ .../src/migrate/DeprecatedStuff.resi | 10 +++++++ .../tools_tests/src/migrate/FileToMigrate.res | 2 ++ .../Migrated_StdlibMigration_Array.res | 1 + tools/src/tools.ml | 27 +++++++++++++++++++ 8 files changed, 55 insertions(+), 2 deletions(-) diff --git a/compiler/ml/cmt_utils.ml b/compiler/ml/cmt_utils.ml index c59b29b3af..2320953b18 100644 --- a/compiler/ml/cmt_utils.ml +++ b/compiler/ml/cmt_utils.ml @@ -1,4 +1,4 @@ -type deprecated_used_context = FunctionCall +type deprecated_used_context = FunctionCall | Reference type deprecated_used = { source_loc: Location.t; diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index 9aadf5b9a8..bfebac842f 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -2285,7 +2285,12 @@ and type_expect_ ?deprecated_context ~context ?in_function ?(recarg = Rejected) match sexp.pexp_desc with | Pexp_ident lid -> let path, desc = - Typetexp.find_value ?deprecated_context env lid.loc lid.txt + Typetexp.find_value + ?deprecated_context: + (match deprecated_context with + | None -> Some Reference + | v -> v) + env lid.loc lid.txt in (if !Clflags.annotations then let dloc = desc.Types.val_loc in diff --git a/tests/tools_tests/src/expected/FileToMigrate.res.expected b/tests/tools_tests/src/expected/FileToMigrate.res.expected index 249e3bab54..2572499f87 100644 --- a/tests/tools_tests/src/expected/FileToMigrate.res.expected +++ b/tests/tools_tests/src/expected/FileToMigrate.res.expected @@ -7,3 +7,5 @@ let someNiceString3 = "abcdefg"->String.slice(~start=2, ~end=5) let shift1 = Array.shift([1, 2, 3]) let shift2 = [1, 2, 3]->Array.shift +let deprecatedThing1 = DeprecatedStuff.Constants.otherThing + diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.res b/tests/tools_tests/src/migrate/DeprecatedStuff.res index de05b9a570..ec1494961e 100644 --- a/tests/tools_tests/src/migrate/DeprecatedStuff.res +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.res @@ -3,3 +3,9 @@ external slice: (string, ~from: int, ~to_: int) => string = "slice" @send external shift: array<'a> => option<'a> = "shift" + +module Constants = { + let otherThing = [2, 3] +} + +let deprecatedThing = [1, 2] diff --git a/tests/tools_tests/src/migrate/DeprecatedStuff.resi b/tests/tools_tests/src/migrate/DeprecatedStuff.resi index 8240d8b62e..1c91c3e439 100644 --- a/tests/tools_tests/src/migrate/DeprecatedStuff.resi +++ b/tests/tools_tests/src/migrate/DeprecatedStuff.resi @@ -14,3 +14,13 @@ external slice: (string, ~from: int, ~to_: int) => string = "slice" migrate: Array.shift(), }) external shift: array<'a> => option<'a> = "shift" + +module Constants: { + let otherThing: array +} + +@deprecated({ + reason: "Use `otherThing` instead.", + migrate: DeprecatedStuff.Constants.otherThing, +}) +let deprecatedThing: array diff --git a/tests/tools_tests/src/migrate/FileToMigrate.res b/tests/tools_tests/src/migrate/FileToMigrate.res index 8f469f6fda..097d2f5c21 100644 --- a/tests/tools_tests/src/migrate/FileToMigrate.res +++ b/tests/tools_tests/src/migrate/FileToMigrate.res @@ -10,3 +10,5 @@ let someNiceString3 = "abcdefg"->DeprecatedStuff.slice(~from=2, ~to_=5) let shift1 = DeprecatedStuff.shift([1, 2, 3]) let shift2 = [1, 2, 3]->DeprecatedStuff.shift + +let deprecatedThing1 = DeprecatedStuff.deprecatedThing diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res index d398c936d3..4f18613d4e 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -166,3 +166,4 @@ let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) + diff --git a/tools/src/tools.ml b/tools/src/tools.ml index d98dafc202..9020b8b1af 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1486,6 +1486,7 @@ module Migrate = struct end let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = + (* Function calls *) let deprecated_function_calls = deprecated_used |> List.filter (fun (d : Cmt_utils.deprecated_used) -> @@ -1499,6 +1500,22 @@ module Migrate = struct deprecated_function_calls |> List.iter (fun ({Cmt_utils.source_loc} as d) -> Hashtbl.replace loc_to_deprecated_fn_call source_loc d); + + (* References *) + let deprecated_references = + deprecated_used + |> List.filter (fun (d : Cmt_utils.deprecated_used) -> + match d.context with + | Some Reference -> true + | _ -> false) + in + let loc_to_deprecated_reference = + Hashtbl.create (List.length deprecated_references) + in + deprecated_references + |> List.iter (fun ({Cmt_utils.source_loc} as d) -> + Hashtbl.replace loc_to_deprecated_reference source_loc d); + let mapper = { Ast_mapper.default_mapper with @@ -1513,6 +1530,16 @@ module Migrate = struct expr = (fun mapper exp -> match exp with + | {pexp_desc = Pexp_ident {loc}} + when Hashtbl.mem loc_to_deprecated_reference loc -> ( + (* Map references to their migration template. *) + let deprecated_info = + Hashtbl.find loc_to_deprecated_reference loc + in + Hashtbl.remove loc_to_deprecated_reference loc; + match deprecated_info.migration_template with + | None -> exp + | Some e -> e) | { pexp_desc = Pexp_apply {funct = {pexp_loc = fn_loc}; args = source_args}; From a721b3297782bdfca9e7b1347cce0ec7626cd03a Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:44:08 +0200 Subject: [PATCH 15/43] do not use unavailable fn --- tools/src/tools.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 9020b8b1af..6e61b69867 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1468,7 +1468,9 @@ module Migrate = struct } ) -> Some (arg_name, label) | _ -> None) - |> StringMap.of_list + |> List.fold_left + (fun map (k, v) -> StringMap.add k v map) + StringMap.empty let apply_migration_template ?(is_pipe = false) mapper template_args source_args = From c25e39878894be012b166dd6cc43b000b94ff26f Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:48:48 +0200 Subject: [PATCH 16/43] format --- .../src/migrate/migrated/Migrated_StdlibMigration_Array.res | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res index 4f18613d4e..d398c936d3 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -166,4 +166,3 @@ let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) - From 81b42923affd557db98efaa0600b2b48ae3ed782 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 22 Jul 2025 23:53:00 +0200 Subject: [PATCH 17/43] do not use unavailable fn --- compiler/ext/ext_list.ml | 4 ++++ compiler/ext/ext_list.mli | 2 ++ tools/src/tools.ml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/ext/ext_list.ml b/compiler/ext/ext_list.ml index 7066f301ba..e7681aee30 100644 --- a/compiler/ext/ext_list.ml +++ b/compiler/ext/ext_list.ml @@ -774,3 +774,7 @@ let filter lst p = | x :: l -> if p x then find (x :: accu) l ~p else find accu l ~p in find [] lst ~p + +let is_empty = function + | [] -> true + | _ :: _ -> false diff --git a/compiler/ext/ext_list.mli b/compiler/ext/ext_list.mli index 61a07c7b38..c5e65149e9 100644 --- a/compiler/ext/ext_list.mli +++ b/compiler/ext/ext_list.mli @@ -231,3 +231,5 @@ val filter : 'a list -> ('a -> bool) -> 'a list val array_list_filter_map : 'a array -> 'b list -> ('a -> 'b -> 'c option) -> 'c list + +val is_empty : 'a list -> bool diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 6e61b69867..2a17b53f3b 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1625,7 +1625,7 @@ module Migrate = struct MapperUtils.get_template_args_to_insert mapper template_args [] in - if List.is_empty template_args_to_insert then + if Ext_list.is_empty template_args_to_insert then { exp with pexp_desc = From 8f1a992e1a8c7ee2be02d0c0ee6e0132fb67d2f4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 12:31:38 +0200 Subject: [PATCH 18/43] Update tools/src/tools.ml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Médi-Rémi Hashim <4295266+mediremi@users.noreply.github.com> --- tools/src/tools.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 2a17b53f3b..beb3230033 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1779,5 +1779,8 @@ module Migrate = struct Printf.fprintf oc "%s" contents; close_out oc; Ok (Filename.basename path ^ ": File migrated successfully")) - | Ok _ -> Ok (Filename.basename path ^ ": File did not need migration") + | Ok (contents, _) -> ( + match outputMode with + | `Stdout -> Ok contents + | `File -> Ok (Filename.basename path ^ ": File did not need migration")) end From ff70ca2afdaa71728251e05ef49f915259b2c4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Thu, 24 Jul 2025 11:40:28 +0200 Subject: [PATCH 19/43] Add Int.Bitwise.land migration --- lib/es6/Stdlib_Int.js | 9 +++++++++ lib/js/Stdlib_Int.js | 9 +++++++++ runtime/Stdlib_Int.res | 12 ++++++++++++ runtime/Stdlib_Int.resi | 18 +++++++++++++++++- .../expected/StdlibMigration_Int.res.expected | 1 + .../src/migrate/StdlibMigration_Int.res | 1 + .../Migrated_StdlibMigration_Array.res | 1 + .../migrated/Migrated_StdlibMigration_Int.res | 1 + 8 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigration_Int.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_Int.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res diff --git a/lib/es6/Stdlib_Int.js b/lib/es6/Stdlib_Int.js index 4993a6297c..fda2d75c1f 100644 --- a/lib/es6/Stdlib_Int.js +++ b/lib/es6/Stdlib_Int.js @@ -62,6 +62,14 @@ function clamp(min, max, value) { } } +function lnot(x) { + return x ^ -1; +} + +let Bitwise = { + lnot: lnot +}; + let Ref = {}; let Constants = { @@ -75,6 +83,7 @@ export { range, rangeWithOptions, clamp, + Bitwise, Ref, } /* No side effect */ diff --git a/lib/js/Stdlib_Int.js b/lib/js/Stdlib_Int.js index 5e6dbb7ced..cece54b2a0 100644 --- a/lib/js/Stdlib_Int.js +++ b/lib/js/Stdlib_Int.js @@ -62,6 +62,14 @@ function clamp(min, max, value) { } } +function lnot(x) { + return x ^ -1; +} + +let Bitwise = { + lnot: lnot +}; + let Ref = {}; let Constants = { @@ -74,5 +82,6 @@ exports.fromString = fromString; exports.range = range; exports.rangeWithOptions = rangeWithOptions; exports.clamp = clamp; +exports.Bitwise = Bitwise; exports.Ref = Ref; /* No side effect */ diff --git a/runtime/Stdlib_Int.res b/runtime/Stdlib_Int.res index 3228ed4114..cef0ee6b27 100644 --- a/runtime/Stdlib_Int.res +++ b/runtime/Stdlib_Int.res @@ -105,6 +105,18 @@ external shiftLeft: (int, int) => int = "%lslint" external shiftRight: (int, int) => int = "%asrint" external shiftRightUnsigned: (int, int) => int = "%lsrint" +module Bitwise = { + external land: (int, int) => int = "%andint" + external lor: (int, int) => int = "%orint" + external lxor: (int, int) => int = "%xorint" + + external lsl: (int, int) => int = "%lslint" + external lsr: (int, int) => int = "%lsrint" + external asr: (int, int) => int = "%asrint" + + let lnot = x => lxor(x, -1) +} + external ignore: int => unit = "%ignore" module Ref = { diff --git a/runtime/Stdlib_Int.resi b/runtime/Stdlib_Int.resi index 384e351287..fa977e9494 100644 --- a/runtime/Stdlib_Int.resi +++ b/runtime/Stdlib_Int.resi @@ -186,7 +186,7 @@ Int.toPrecisionWithPrecision(1, ~digits=2) // "1.0" - `RangeError`: If `digits` is not between 1 and 100 (inclusive). Implementations are allowed to support larger and smaller values as well. ECMA-262 only requires a precision of up to 21 significant digits. - + */ @send @deprecated("Use `toPrecision` instead") external toPrecisionWithPrecision: (int, ~digits: int) => string = "toPrecision" @@ -471,6 +471,22 @@ Int.shiftRightUnsigned(4, 1) == 2 */ external shiftRightUnsigned: (int, int) => int = "%lsrint" +module Bitwise = { + @deprecated({ + reason: "Use `Int.bitwiseAnd` instead", + migrate: Int.bitwiseAnd + }) + external land: (int, int) => int = "%andint" + external lor: (int, int) => int = "%orint" + external lxor: (int, int) => int = "%xorint" + + external lsl: (int, int) => int = "%lslint" + external lsr: (int, int) => int = "%lsrint" + external asr: (int, int) => int = "%asrint" + + let lnot: int => int +} + /** `ignore(int)` ignores the provided int and returns unit. diff --git a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected new file mode 100644 index 0000000000..37413b15e4 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected @@ -0,0 +1 @@ +StdlibMigration_Int.res: File did not need migration diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Int.res b/tests/tools_tests/src/migrate/StdlibMigration_Int.res new file mode 100644 index 0000000000..983e50fd57 --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_Int.res @@ -0,0 +1 @@ +let result = Int.Bitwise.land(1, 2) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res index d398c936d3..4f18613d4e 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -166,3 +166,4 @@ let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) + diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res new file mode 100644 index 0000000000..37413b15e4 --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res @@ -0,0 +1 @@ +StdlibMigration_Int.res: File did not need migration From 1a9b287c517e8cdd6003721ce1a8c03305af4805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Thu, 24 Jul 2025 13:13:10 +0200 Subject: [PATCH 20/43] Add missing brackets to migration --- runtime/Stdlib_Int.resi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/Stdlib_Int.resi b/runtime/Stdlib_Int.resi index fa977e9494..0705436e0a 100644 --- a/runtime/Stdlib_Int.resi +++ b/runtime/Stdlib_Int.resi @@ -474,7 +474,7 @@ external shiftRightUnsigned: (int, int) => int = "%lsrint" module Bitwise = { @deprecated({ reason: "Use `Int.bitwiseAnd` instead", - migrate: Int.bitwiseAnd + migrate: Int.bitwiseAnd() }) external land: (int, int) => int = "%andint" external lor: (int, int) => int = "%orint" From d9133d21524ddf97824e0ff27e4d230668dfb3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Thu, 24 Jul 2025 13:18:37 +0200 Subject: [PATCH 21/43] Add migrations for remaining Int.Bitwise functions --- runtime/Stdlib_Int.resi | 24 +++++++++++++++++++ .../expected/StdlibMigration_Int.res.expected | 9 ++++++- .../src/migrate/StdlibMigration_Int.res | 6 +++++ .../migrated/Migrated_StdlibMigration_Int.res | 9 ++++++- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/runtime/Stdlib_Int.resi b/runtime/Stdlib_Int.resi index 0705436e0a..c3b61bb1af 100644 --- a/runtime/Stdlib_Int.resi +++ b/runtime/Stdlib_Int.resi @@ -477,13 +477,37 @@ module Bitwise = { migrate: Int.bitwiseAnd() }) external land: (int, int) => int = "%andint" + @deprecated({ + reason: "Use `Int.bitwiseOr` instead", + migrate: Int.bitwiseOr() + }) external lor: (int, int) => int = "%orint" + @deprecated({ + reason: "Use `Int.bitwiseXor` instead", + migrate: Int.bitwiseXor() + }) external lxor: (int, int) => int = "%xorint" + @deprecated({ + reason: "Use `Int.shiftLeft` instead", + migrate: Int.shiftLeft() + }) external lsl: (int, int) => int = "%lslint" + @deprecated({ + reason: "Use `Int.shiftRightUnsigned` instead", + migrate: Int.shiftRightUnsigned() + }) external lsr: (int, int) => int = "%lsrint" + @deprecated({ + reason: "Use `Int.shiftRight` instead", + migrate: Int.shiftRight() + }) external asr: (int, int) => int = "%asrint" + @deprecated({ + reason: "Use `Int.bitwiseNot` instead", + migrate: Int.bitwiseNot() + }) let lnot: int => int } diff --git a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected index 37413b15e4..d38801fc09 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected @@ -1 +1,8 @@ -StdlibMigration_Int.res: File did not need migration +let result = Int.bitwiseAnd(1, 2) +let result = Int.bitwiseOr(1, 2) +let result = Int.bitwiseXor(1, 2) +let result = Int.shiftLeft(1, 2) +let result = Int.shiftRightUnsigned(1, 2) +let result = Int.shiftLeft(1, 2) +let result = Int.bitwiseNot(0) + diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Int.res b/tests/tools_tests/src/migrate/StdlibMigration_Int.res index 983e50fd57..8c587f823e 100644 --- a/tests/tools_tests/src/migrate/StdlibMigration_Int.res +++ b/tests/tools_tests/src/migrate/StdlibMigration_Int.res @@ -1 +1,7 @@ let result = Int.Bitwise.land(1, 2) +let result = Int.Bitwise.lor(1, 2) +let result = Int.Bitwise.lxor(1, 2) +let result = Int.Bitwise.lsl(1, 2) +let result = Int.Bitwise.lsr(1, 2) +let result = Int.Bitwise.asr(1, 2) +let result = Int.Bitwise.lnot(0) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res index 37413b15e4..677d7e37e9 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res @@ -1 +1,8 @@ -StdlibMigration_Int.res: File did not need migration +let result = Int.bitwiseAnd(1, 2) +let result = Int.bitwiseOr(1, 2) +let result = Int.bitwiseXor(1, 2) +let result = Int.shiftLeft(1, 2) +let result = Int.shiftRightUnsigned(1, 2) +let result = Int.shiftRight(1, 2) +let result = Int.bitwiseNot(0) + From f2c9eb580dff215c627386174df80ee9d3ba695a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Thu, 24 Jul 2025 13:21:14 +0200 Subject: [PATCH 22/43] Remove newline in toPrecisionWithPrecision doc comment --- runtime/Stdlib_Int.resi | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/Stdlib_Int.resi b/runtime/Stdlib_Int.resi index c3b61bb1af..67c2ae3f56 100644 --- a/runtime/Stdlib_Int.resi +++ b/runtime/Stdlib_Int.resi @@ -186,7 +186,6 @@ Int.toPrecisionWithPrecision(1, ~digits=2) // "1.0" - `RangeError`: If `digits` is not between 1 and 100 (inclusive). Implementations are allowed to support larger and smaller values as well. ECMA-262 only requires a precision of up to 21 significant digits. - */ @send @deprecated("Use `toPrecision` instead") external toPrecisionWithPrecision: (int, ~digits: int) => string = "toPrecision" From c7905413f79b4c616313232c52069192b5159d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Thu, 24 Jul 2025 13:26:50 +0200 Subject: [PATCH 23/43] Add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6d03896f..a1c4f5ae69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ #### :nail_care: Polish - Apply heuristic to suggest using JSX fragments where we guess that might be what the user wanted. https://github.com/rescript-lang/rescript/pull/7714 +- `Int.Bitwise` functions have been deprecated. https://github.com/rescript-lang/rescript/pull/7705 # 12.0.0-beta.3 From b682e3f145734de17bd36a5ea59b35cc9027c82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Thu, 24 Jul 2025 13:41:02 +0200 Subject: [PATCH 24/43] Update StdlibMigration_Int expectation --- tests/tools_tests/src/expected/StdlibMigration_Int.res.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected index d38801fc09..677d7e37e9 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected @@ -3,6 +3,6 @@ let result = Int.bitwiseOr(1, 2) let result = Int.bitwiseXor(1, 2) let result = Int.shiftLeft(1, 2) let result = Int.shiftRightUnsigned(1, 2) -let result = Int.shiftLeft(1, 2) +let result = Int.shiftRight(1, 2) let result = Int.bitwiseNot(0) From e7017b5992b6b70988e850504c85ef3c0dcfe9aa Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 13:42:45 +0200 Subject: [PATCH 25/43] emit clarifying comment in migrated files --- .../src/migrate/migrated/Migrated_StdlibMigration_Array.res | 2 ++ tests/tools_tests/test.sh | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res index 4f18613d4e..ae2d5dd387 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -1,3 +1,5 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_Array.res. let shift1 = [1, 2, 3]->Array.shift let shift2 = Array.shift([1, 2, 3]) diff --git a/tests/tools_tests/test.sh b/tests/tools_tests/test.sh index 79cbb36efe..63c66b22a7 100755 --- a/tests/tools_tests/test.sh +++ b/tests/tools_tests/test.sh @@ -46,7 +46,8 @@ done for file in src/migrate/StdlibMigration_*.res; do expected_file="src/expected/$(basename $file).expected" output="src/migrate/migrated/Migrated_$(basename $file)" - cp -f "$expected_file" "$output" + echo "// This file is autogenerated so it can be type checked. +// It's the migrated version of $file." > "$output" && cat "$expected_file" >> "$output" done warningYellow='\033[0;33m' From a91f76037a3210219515d7aa2a5a4e4119ebd833 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 13:45:10 +0200 Subject: [PATCH 26/43] commit new output --- .../src/expected/DeprecatedStuff.res.expected | 13 ++++++++- .../expected/DeprecatedStuff.resi.expected | 28 ++++++++++++++++++- .../migrated/Migrated_StdlibMigration_Int.res | 2 ++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/tools_tests/src/expected/DeprecatedStuff.res.expected b/tests/tools_tests/src/expected/DeprecatedStuff.res.expected index 4e72aff940..3a9e14c4f2 100644 --- a/tests/tools_tests/src/expected/DeprecatedStuff.res.expected +++ b/tests/tools_tests/src/expected/DeprecatedStuff.res.expected @@ -1 +1,12 @@ -DeprecatedStuff.res: File did not need migration +@send +external slice: (string, ~from: int, ~to_: int) => string = "slice" + +@send +external shift: array<'a> => option<'a> = "shift" + +module Constants = { + let otherThing = [2, 3] +} + +let deprecatedThing = [1, 2] + diff --git a/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected b/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected index 7e76b1c053..df7f2f270f 100644 --- a/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected +++ b/tests/tools_tests/src/expected/DeprecatedStuff.resi.expected @@ -1 +1,27 @@ -DeprecatedStuff.resi: File did not need migration +@deprecated({ + reason: "Use `String.slice` instead", + migrate: String.slice( + ~start=%insert.labelledArgument("from"), + ~end=%insert.labelledArgument("to_"), + ), +}) +@send +external slice: (string, ~from: int, ~to_: int) => string = "slice" + +@send +@deprecated({ + reason: "Use `Array.shift` instead.", + migrate: Array.shift(), +}) +external shift: array<'a> => option<'a> = "shift" + +module Constants: { + let otherThing: array +} + +@deprecated({ + reason: "Use `otherThing` instead.", + migrate: DeprecatedStuff.Constants.otherThing, +}) +let deprecatedThing: array + diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res index 677d7e37e9..bd3883b80d 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res @@ -1,3 +1,5 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_Int.res. let result = Int.bitwiseAnd(1, 2) let result = Int.bitwiseOr(1, 2) let result = Int.bitwiseXor(1, 2) From 42846c9f004f618f8f061e53bfc97c34d0a1fe12 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 13:50:52 +0200 Subject: [PATCH 27/43] formatting --- runtime/Stdlib_Int.resi | 16 ++++++++-------- .../migrated/Migrated_StdlibMigration_Array.res | 1 - .../migrated/Migrated_StdlibMigration_Int.res | 1 - tests/tools_tests/test.sh | 1 + 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/runtime/Stdlib_Int.resi b/runtime/Stdlib_Int.resi index 67c2ae3f56..9959990901 100644 --- a/runtime/Stdlib_Int.resi +++ b/runtime/Stdlib_Int.resi @@ -470,42 +470,42 @@ Int.shiftRightUnsigned(4, 1) == 2 */ external shiftRightUnsigned: (int, int) => int = "%lsrint" -module Bitwise = { +module Bitwise: { @deprecated({ reason: "Use `Int.bitwiseAnd` instead", - migrate: Int.bitwiseAnd() + migrate: Int.bitwiseAnd(), }) external land: (int, int) => int = "%andint" @deprecated({ reason: "Use `Int.bitwiseOr` instead", - migrate: Int.bitwiseOr() + migrate: Int.bitwiseOr(), }) external lor: (int, int) => int = "%orint" @deprecated({ reason: "Use `Int.bitwiseXor` instead", - migrate: Int.bitwiseXor() + migrate: Int.bitwiseXor(), }) external lxor: (int, int) => int = "%xorint" @deprecated({ reason: "Use `Int.shiftLeft` instead", - migrate: Int.shiftLeft() + migrate: Int.shiftLeft(), }) external lsl: (int, int) => int = "%lslint" @deprecated({ reason: "Use `Int.shiftRightUnsigned` instead", - migrate: Int.shiftRightUnsigned() + migrate: Int.shiftRightUnsigned(), }) external lsr: (int, int) => int = "%lsrint" @deprecated({ reason: "Use `Int.shiftRight` instead", - migrate: Int.shiftRight() + migrate: Int.shiftRight(), }) external asr: (int, int) => int = "%asrint" @deprecated({ reason: "Use `Int.bitwiseNot` instead", - migrate: Int.bitwiseNot() + migrate: Int.bitwiseNot(), }) let lnot: int => int } diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res index ae2d5dd387..52a6757808 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Array.res @@ -168,4 +168,3 @@ let reduceRight2 = Array.reduceRight([1, 2, 3], 0, (acc, x) => acc + x) let reduceRighti1 = [1, 2, 3]->Array.reduceRightWithIndex(0, (acc, x, i) => acc + x + i) let reduceRighti2 = Array.reduceRightWithIndex([1, 2, 3], 0, (acc, x, i) => acc + x + i) - diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res index bd3883b80d..4d1757d8ce 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res @@ -7,4 +7,3 @@ let result = Int.shiftLeft(1, 2) let result = Int.shiftRightUnsigned(1, 2) let result = Int.shiftRight(1, 2) let result = Int.bitwiseNot(0) - diff --git a/tests/tools_tests/test.sh b/tests/tools_tests/test.sh index 63c66b22a7..4e44f42170 100755 --- a/tests/tools_tests/test.sh +++ b/tests/tools_tests/test.sh @@ -48,6 +48,7 @@ for file in src/migrate/StdlibMigration_*.res; do output="src/migrate/migrated/Migrated_$(basename $file)" echo "// This file is autogenerated so it can be type checked. // It's the migrated version of $file." > "$output" && cat "$expected_file" >> "$output" + ../../cli/rescript.js format "$output" done warningYellow='\033[0;33m' From 76cecf9839f06a362fcf30b3ef0abd122aeb7f08 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 13:55:40 +0200 Subject: [PATCH 28/43] avoid some warnings --- .../src/expected/StdlibMigration_Int.res.expected | 14 +++++++------- .../src/migrate/StdlibMigration_Int.res | 14 +++++++------- .../migrated/Migrated_StdlibMigration_Int.res | 14 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected index 677d7e37e9..49289fa30b 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_Int.res.expected @@ -1,8 +1,8 @@ -let result = Int.bitwiseAnd(1, 2) -let result = Int.bitwiseOr(1, 2) -let result = Int.bitwiseXor(1, 2) -let result = Int.shiftLeft(1, 2) -let result = Int.shiftRightUnsigned(1, 2) -let result = Int.shiftRight(1, 2) -let result = Int.bitwiseNot(0) +let result1 = Int.bitwiseAnd(1, 2) +let result2 = Int.bitwiseOr(1, 2) +let result3 = Int.bitwiseXor(1, 2) +let result4 = Int.shiftLeft(1, 2) +let result5 = Int.shiftRightUnsigned(1, 2) +let result6 = Int.shiftRight(1, 2) +let result7 = Int.bitwiseNot(0) diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Int.res b/tests/tools_tests/src/migrate/StdlibMigration_Int.res index 8c587f823e..b76172c1a1 100644 --- a/tests/tools_tests/src/migrate/StdlibMigration_Int.res +++ b/tests/tools_tests/src/migrate/StdlibMigration_Int.res @@ -1,7 +1,7 @@ -let result = Int.Bitwise.land(1, 2) -let result = Int.Bitwise.lor(1, 2) -let result = Int.Bitwise.lxor(1, 2) -let result = Int.Bitwise.lsl(1, 2) -let result = Int.Bitwise.lsr(1, 2) -let result = Int.Bitwise.asr(1, 2) -let result = Int.Bitwise.lnot(0) +let result1 = Int.Bitwise.land(1, 2) +let result2 = Int.Bitwise.lor(1, 2) +let result3 = Int.Bitwise.lxor(1, 2) +let result4 = Int.Bitwise.lsl(1, 2) +let result5 = Int.Bitwise.lsr(1, 2) +let result6 = Int.Bitwise.asr(1, 2) +let result7 = Int.Bitwise.lnot(0) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res index 4d1757d8ce..43b70137c8 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Int.res @@ -1,9 +1,9 @@ // This file is autogenerated so it can be type checked. // It's the migrated version of src/migrate/StdlibMigration_Int.res. -let result = Int.bitwiseAnd(1, 2) -let result = Int.bitwiseOr(1, 2) -let result = Int.bitwiseXor(1, 2) -let result = Int.shiftLeft(1, 2) -let result = Int.shiftRightUnsigned(1, 2) -let result = Int.shiftRight(1, 2) -let result = Int.bitwiseNot(0) +let result1 = Int.bitwiseAnd(1, 2) +let result2 = Int.bitwiseOr(1, 2) +let result3 = Int.bitwiseXor(1, 2) +let result4 = Int.shiftLeft(1, 2) +let result5 = Int.shiftRightUnsigned(1, 2) +let result6 = Int.shiftRight(1, 2) +let result7 = Int.bitwiseNot(0) From c0c591feab061c376e32ec5824be41f6c0f467e6 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 13:58:05 +0200 Subject: [PATCH 29/43] update test output --- tests/analysis_tests/tests/src/expected/Completion.res.txt | 2 +- .../tests/src/expected/CompletionInferValues.res.txt | 4 ++-- tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt | 4 ++-- .../tests/src/expected/CompletionPipeChain.res.txt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/analysis_tests/tests/src/expected/Completion.res.txt b/tests/analysis_tests/tests/src/expected/Completion.res.txt index c0b1160f66..e43ce4c40c 100644 --- a/tests/analysis_tests/tests/src/expected/Completion.res.txt +++ b/tests/analysis_tests/tests/src/expected/Completion.res.txt @@ -2471,7 +2471,7 @@ Path t "kind": 12, "tags": [1], "detail": "(int, ~digits: int) => string", - "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n \n"} + "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n"} }, { "label": "Int.toPrecision", "kind": 12, diff --git a/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt b/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt index dc885d8485..cbc06886c9 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt @@ -34,7 +34,7 @@ Path t "kind": 12, "tags": [1], "detail": "(int, ~digits: int) => string", - "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n \n"} + "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n"} }, { "label": "Int.toPrecision", "kind": 12, @@ -352,7 +352,7 @@ Path t "kind": 12, "tags": [1], "detail": "(int, ~digits: int) => string", - "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n \n"} + "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n"} }, { "label": "Int.toPrecision", "kind": 12, diff --git a/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt b/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt index e9907b0a2b..3251ada81d 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt @@ -243,7 +243,7 @@ Path "kind": 12, "tags": [1], "detail": "(int, ~digits: int) => string", - "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n \n"} + "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n"} }, { "label": "Int.bitwiseAnd", "kind": 12, @@ -413,7 +413,7 @@ Path "kind": 12, "tags": [1], "detail": "(int, ~digits: int) => string", - "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n \n"} + "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n"} }, { "label": "Int.bitwiseAnd", "kind": 12, diff --git a/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt b/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt index 4fb8e0849f..1519636e45 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt @@ -347,7 +347,7 @@ Path t "kind": 12, "tags": [1], "detail": "(int, ~digits: int) => string", - "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n \n"} + "documentation": {"kind": "markdown", "value": "Deprecated: Use `toPrecision` instead\n\n\n`toPrecisionWithPrecision(n, ~digits)` return a `string` representing the giver value with\nprecision. `digits` specifies the number of significant digits. See [`Number.toPrecision`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision) on MDN.\n\n## Examples\n\n```rescript\nInt.toPrecisionWithPrecision(100, ~digits=2) // \"1.0e+2\"\nInt.toPrecisionWithPrecision(1, ~digits=2) // \"1.0\"\n```\n\n## Exceptions\n\n- `RangeError`: If `digits` is not between 1 and 100 (inclusive).\n Implementations are allowed to support larger and smaller values as well.\n ECMA-262 only requires a precision of up to 21 significant digits.\n"} }, { "label": "Int.toPrecision", "kind": 12, From 23dc15422d490394aee54567d4fd462a5943c886 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 18:10:03 +0200 Subject: [PATCH 30/43] migrations for Js_string2 --- runtime/Js_string2.res | 218 +++++++++++++++++- runtime/Stdlib_Array.res | 2 + runtime/Stdlib_Array.resi | 11 + runtime/Stdlib_String.resi | 2 +- ...dlibMigrationNoCompile_String.res.expected | 12 + .../StdlibMigration_String.res.expected | 126 ++++++++++ .../StdlibMigrationNoCompile_String.res | 11 + .../src/migrate/StdlibMigration_String.res | 125 ++++++++++ .../Migrated_StdlibMigration_String.res | 127 ++++++++++ 9 files changed, 625 insertions(+), 9 deletions(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigrationNoCompile_String.res.expected create mode 100644 tests/tools_tests/src/expected/StdlibMigration_String.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigrationNoCompile_String.res create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_String.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_String.res diff --git a/runtime/Js_string2.res b/runtime/Js_string2.res index 700b54a0c4..34257c5a3b 100644 --- a/runtime/Js_string2.res +++ b/runtime/Js_string2.res @@ -36,6 +36,10 @@ Js.String2.make(3.5) == "3.5" Js.String2.make([1, 2, 3]) == "1,2,3" ``` */ +@deprecated({ + reason: "Use `String.make` instead", + migrate: String.make(), +}) @val external make: 'a => t = "String" @@ -57,6 +61,10 @@ Js.String2.fromCharCode(0xd55c) == `한` Js.String2.fromCharCode(-64568) == `ψ` ``` */ +@deprecated({ + reason: "Use `String.fromCharCode` instead", + migrate: String.fromCharCode(), +}) @val external fromCharCode: int => t = "String.fromCharCode" @@ -67,7 +75,12 @@ corresponding to the given numbers, using the same rules as `fromCharCode`. See [`String.fromCharCode`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode) on MDN. */ -@val @variadic +@deprecated({ + reason: "Use `String.fromCharCodeMany` instead", + migrate: String.fromCharCodeMany(), +}) +@val +@variadic external fromCharCodeMany: array => t = "String.fromCharCode" /** @@ -89,6 +102,10 @@ Js.String2.fromCodePoint(0xd55c) == `한` Js.String2.fromCodePoint(0x1f63a) == `😺` ``` */ +@deprecated({ + reason: "Use `String.fromCodePoint` instead", + migrate: String.fromCodePoint(), +}) @val external fromCodePoint: int => t = "String.fromCodePoint" @@ -106,7 +123,12 @@ on MDN. Js.String2.fromCodePointMany([0xd55c, 0xae00, 0x1f63a]) == `한글😺` ``` */ -@val @variadic +@deprecated({ + reason: "Use `String.fromCodePointMany` instead", + migrate: String.fromCodePointMany(), +}) +@val +@variadic external fromCodePointMany: array => t = "String.fromCodePoint" /* String.raw: ES2015, meant to be used with template strings, not directly */ @@ -123,6 +145,10 @@ on MDN. Js.String2.length("abcd") == 4 ``` */ +@deprecated({ + reason: "Use `String.length` instead", + migrate: String.length(), +}) @get external length: t => int = "length" @@ -139,6 +165,10 @@ Js.String2.get("Reason", 4) == "o" Js.String2.get(`Rẽasöń`, 5) == `ń` ``` */ +@deprecated({ + reason: "Use `String.getUnsafe` instead. Or use `String.get` for a safe version that returns an option.", + migrate: String.getUnsafe(), +}) @get_index external get: (t, int) => t = "" @@ -159,6 +189,10 @@ Js.String2.charAt("Reason", 12) == "" Js.String2.charAt(`Rẽasöń`, 5) == `ń` ``` */ +@deprecated({ + reason: "Use `String.charAt` instead", + migrate: String.charAt(), +}) @send external charAt: (t, int) => t = "charAt" @@ -179,6 +213,10 @@ Js.String2.charCodeAt(`😺`, 0) == 0xd83d->Belt.Int.toFloat Js.String2.codePointAt(`😺`, 0) == Some(0x1f63a) ``` */ +@deprecated({ + reason: "Use `String.charCodeAt` instead", + migrate: String.charCodeAt(), +}) @send external charCodeAt: (t, int) => float = "charCodeAt" @@ -198,6 +236,10 @@ Js.String2.codePointAt(`¿😺?`, 1) == Some(0x1f63a) Js.String2.codePointAt("abc", 5) == None ``` */ +@deprecated({ + reason: "Use `String.codePointAt` instead", + migrate: String.codePointAt(), +}) @send external codePointAt: (t, int) => option = "codePointAt" @@ -214,6 +256,10 @@ on MDN. Js.String2.concat("cow", "bell") == "cowbell" ``` */ +@deprecated({ + reason: "Use `String.concat` instead", + migrate: String.concat(), +}) @send external concat: (t, t) => t = "concat" @@ -230,7 +276,12 @@ on MDN. Js.String2.concatMany("1st", ["2nd", "3rd", "4th"]) == "1st2nd3rd4th" ``` */ -@send @variadic +@deprecated({ + reason: "Use `String.concatMany` instead", + migrate: String.concatMany(), +}) +@send +@variadic external concatMany: (t, array) => t = "concat" /** @@ -247,6 +298,10 @@ Js.String2.endsWith("ReScript", "Script") == true Js.String2.endsWith("C++", "Script") == false ``` */ +@deprecated({ + reason: "Use `String.endsWith` instead", + migrate: String.endsWith(), +}) @send external endsWith: (t, t) => bool = "endsWith" @@ -268,6 +323,10 @@ Js.String2.endsWithFrom("abcde", "cde", 99) == true Js.String2.endsWithFrom("example.dat", "ple", 7) == true ``` */ +@deprecated({ + reason: "Use `String.endsWithFrom` instead", + migrate: String.endsWithFrom(), +}) @send external endsWithFrom: (t, t, int) => bool = "endsWith" @@ -287,6 +346,10 @@ Js.String2.includes("programmer", "pro") == true Js.String2.includes("programmer.dat", "xyz") == false ``` */ +@deprecated({ + reason: "Use `String.includes` instead", + migrate: String.includes(), +}) @send external includes: (t, t) => bool = "includes" @@ -306,6 +369,10 @@ Js.String2.includesFrom("programmer", "gram", 4) == false Js.String2.includesFrom(`대한민국`, `한`, 1) == true ``` */ +@deprecated({ + reason: "Use `String.includesFrom` instead", + migrate: String.includesFrom(), +}) @send external includesFrom: (t, t, int) => bool = "includes" @@ -325,6 +392,10 @@ Js.String2.indexOf("beekeeper", "ee") == 1 Js.String2.indexOf("bookseller", "xyz") == -1 ``` */ +@deprecated({ + reason: "Use `String.indexOf` instead", + migrate: String.indexOf(), +}) @send external indexOf: (t, t) => int = "indexOf" @@ -346,6 +417,10 @@ Js.String2.indexOfFrom("bookseller", "sell", 2) == 4 Js.String2.indexOfFrom("bookseller", "sell", 5) == -1 ``` */ +@deprecated({ + reason: "Use `String.indexOfFrom` instead", + migrate: String.indexOfFrom(), +}) @send external indexOfFrom: (t, t, int) => int = "indexOf" @@ -366,6 +441,10 @@ Js.String2.lastIndexOf("beekeeper", "ee") == 4 Js.String2.lastIndexOf("abcdefg", "xyz") == -1 ``` */ +@deprecated({ + reason: "Use `String.lastIndexOf` instead", + migrate: String.lastIndexOf(), +}) @send external lastIndexOf: (t, t) => int = "lastIndexOf" @@ -387,6 +466,10 @@ Js.String2.lastIndexOfFrom("beekeeper", "ee", 3) == 1 Js.String2.lastIndexOfFrom("abcdefg", "xyz", 4) == -1 ``` */ +@deprecated({ + reason: "Use `String.lastIndexOfFrom` instead", + migrate: String.lastIndexOfFrom(), +}) @send external lastIndexOfFrom: (t, t, int) => int = "lastIndexOf" @@ -409,6 +492,10 @@ Js.String2.localeCompare("cat", "cat") == 0.0 Js.String2.localeCompare("CAT", "cat") > 0.0 ``` */ +@deprecated({ + reason: "Use `String.localeCompare` instead", + migrate: String.localeCompare(), +}) @send external localeCompare: (t, t) => float = "localeCompare" @@ -434,7 +521,12 @@ Js.String2.match_("Today is 2018-04-05.", /(\d+)-(\d+)-(\d+)/) == Js.String2.match_("The large container.", /b[aeiou]g/) == None ``` */ -@send @return({null_to_opt: null_to_opt}) +@deprecated({ + reason: "Use `String.match` instead", + migrate: String.match(), +}) +@send +@return({null_to_opt: null_to_opt}) external match_: (t, Js_re.t) => option>> = "match" /** @@ -448,6 +540,10 @@ See [`String.normalize`](https://developer.mozilla.org/en-US/docs/Web/JavaScript on MDN. See also [Unicode technical report #15](https://unicode.org/reports/tr15/) for details. */ +@deprecated({ + reason: "Use `String.normalize` instead", + migrate: String.normalize(), +}) @send external normalize: t => t = "normalize" @@ -462,6 +558,10 @@ specified form of normalization, which may be one of: See [`String.normalize`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize) on MDN. See also [Unicode technical report #15](https://unicode.org/reports/tr15/) for details. */ +@deprecated({ + reason: "Use `String.normalizeByForm` instead", + migrate: String.normalizeByForm(), +}) @send external normalizeByForm: (t, t) => t = "normalize" @@ -479,6 +579,10 @@ Js.String2.repeat("ha", 3) == "hahaha" Js.String2.repeat("empty", 0) == "" ``` */ +@deprecated({ + reason: "Use `String.repeat` instead", + migrate: String.repeat(), +}) @send external repeat: (t, int) => t = "repeat" @@ -498,6 +602,10 @@ Js.String2.replace("old string", "old", "new") == "new string" Js.String2.replace("the cat and the dog", "the", "this") == "this cat and the dog" ``` */ +@deprecated({ + reason: "Use `String.replace` instead", + migrate: String.replace(), +}) @send external replace: (t, t, t) => t = "replace" @@ -515,6 +623,10 @@ Js.String2.replaceByRe("vowels be gone", /[aeiou]/g, "x") == "vxwxls bx gxnx" Js.String2.replaceByRe("Juan Fulano", /(\w+) (\w+)/, "$2, $1") == "Fulano, Juan" ``` */ +@deprecated({ + reason: "Use `String.replaceRegExp` instead", + migrate: String.replaceRegExp(), +}) @send external replaceByRe: (t, Js_re.t, t) => t = "replace" @@ -537,6 +649,10 @@ let matchFn = (matchPart, _offset, _wholeString) => Js.String2.toUpperCase(match Js.String2.unsafeReplaceBy0(str, re, matchFn) == "bEAUtIfUl vOwEls" ``` */ +@deprecated({ + reason: "Use `String.replaceRegExpBy0Unsafe` instead", + migrate: String.replaceRegExpBy0Unsafe(), +}) @send external unsafeReplaceBy0: (t, Js_re.t, (t, int, t) => t) => t = "replace" @@ -562,6 +678,10 @@ let matchFn = (_match, part1, _offset, _wholeString) => { Js.String2.unsafeReplaceBy1(str, re, matchFn) == "Jony is 41" ``` */ +@deprecated({ + reason: "Use `String.replaceRegExpBy1Unsafe` instead", + migrate: String.replaceRegExpBy1Unsafe(), +}) @send external unsafeReplaceBy1: (t, Js_re.t, (t, t, int, t) => t) => t = "replace" @@ -590,6 +710,10 @@ let matchFn = (_match, p1, p2, _offset, _wholeString) => { Js.String2.unsafeReplaceBy2(str, re, matchFn) == "42" ``` */ +@deprecated({ + reason: "Use `String.replaceRegExpBy2Unsafe` instead", + migrate: String.replaceRegExpBy2Unsafe(), +}) @send external unsafeReplaceBy2: (t, Js_re.t, (t, t, t, int, t) => t) => t = "replace" @@ -603,6 +727,10 @@ matched. See [`String.replace`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) on MDN. */ +@deprecated({ + reason: "Use `String.replaceRegExpBy3Unsafe` instead", + migrate: String.replaceRegExpBy3Unsafe(), +}) @send external unsafeReplaceBy3: (t, Js_re.t, (t, t, t, t, int, t) => t) => t = "replace" @@ -620,6 +748,10 @@ Js.String2.search("testing 1 2 3", /\d+/) == 8 Js.String2.search("no numbers", /\d+/) == -1 ``` */ +@deprecated({ + reason: "Use `String.search` instead", + migrate: String.search(), +}) @send external search: (t, Js_re.t) => int = "search" @@ -641,6 +773,13 @@ Js.String2.slice("abcdefg", ~from=-4, ~to_=-2) == "de" Js.String2.slice("abcdefg", ~from=5, ~to_=1) == "" ``` */ +@deprecated({ + reason: "Use `String.slice` instead", + migrate: String.slice( + ~start=%insert.labelledArgument("from"), + ~end=%insert.labelledArgument("to_"), + ), +}) @send external slice: (t, ~from: int, ~to_: int) => t = "slice" @@ -660,6 +799,10 @@ Js.String2.sliceToEnd("abcdefg", ~from=-2) == "fg" Js.String2.sliceToEnd("abcdefg", ~from=7) == "" ``` */ +@deprecated({ + reason: "Use `String.sliceToEnd` instead", + migrate: String.sliceToEnd(~start=%insert.labelledArgument("from")), +}) @send external sliceToEnd: (t, ~from: int) => t = "slice" @@ -679,6 +822,10 @@ Js.String2.split("good::bad as great::awful", "::") == ["good", "bad as great", Js.String2.split("has-no-delimiter", ";") == ["has-no-delimiter"] ``` */ +@deprecated({ + reason: "Use `String.split` instead", + migrate: String.split(), +}) @send external split: (t, t) => array = "split" @@ -691,6 +838,10 @@ splitAtMost "ant/bee/cat/dog/elk" "/" ~limit: 0 = [| |];; splitAtMost "ant/bee/cat/dog/elk" "/" ~limit: 9 = [|"ant"; "bee"; "cat"; "dog"; "elk"|];; ``` */ +@deprecated({ + reason: "Use `String.splitAtMost` instead", + migrate: String.splitAtMost(), +}) @send external splitAtMost: (t, t, ~limit: int) => array = "split" @@ -712,6 +863,10 @@ Js.String2.splitByRe("art; bed , cog ;dad", /\s*[,;]\s*TODO/) == [ ] ``` */ +@deprecated({ + reason: "Use `String.splitByRegExp` instead", + migrate: String.splitByRegExp(), +}) @send external splitByRe: (t, Js_re.t) => array> = "split" @@ -743,6 +898,10 @@ Js.String2.splitByReAtMost("one: two: three: four", /\s*:\s*TODO/, ~limit=8) == ] ``` */ +@deprecated({ + reason: "Use `String.splitByRegExpAtMost` instead", + migrate: String.splitByRegExpAtMost(), +}) @send external splitByReAtMost: (t, Js_re.t, ~limit: int) => array> = "split" @@ -761,6 +920,10 @@ Js.String2.startsWith("ReScript", "") == true Js.String2.startsWith("JavaScript", "Re") == false ``` */ +@deprecated({ + reason: "Use `String.startsWith` instead", + migrate: String.startsWith(), +}) @send external startsWith: (t, t) => bool = "startsWith" @@ -780,6 +943,10 @@ Js.String2.startsWithFrom("ReScript", "", 2) == true Js.String2.startsWithFrom("JavaScript", "Scri", 2) == false ``` */ +@deprecated({ + reason: "Use `String.startsWithFrom` instead", + migrate: String.startsWithFrom(), +}) @send external startsWithFrom: (t, t, int) => bool = "startsWith" @@ -803,7 +970,7 @@ Js.String2.substr("abcdefghij", ~from=-3) == "hij" Js.String2.substr("abcdefghij", ~from=12) == "" ``` */ -@send +@deprecated("Use `String.substring` instead") @send external substr: (t, ~from: int) => t = "substr" /** @@ -827,7 +994,7 @@ Js.String2.substrAtMost("abcdefghij", ~from=-3, ~length=4) == "hij" Js.String2.substrAtMost("abcdefghij", ~from=12, ~length=2) == "" ``` */ -@send +@deprecated("Use `String.substringAtMost` instead") @send external substrAtMost: (t, ~from: int, ~length: int) => t = "substr" /** @@ -847,6 +1014,13 @@ Js.String2.substring("playground", ~from=6, ~to_=3) == "ygr" Js.String2.substring("playground", ~from=4, ~to_=12) == "ground" ``` */ +@deprecated({ + reason: "Use `String.substring` instead", + migrate: String.substring( + ~start=%insert.labelledArgument("from"), + ~end=%insert.labelledArgument("to_"), + ), +}) @send external substring: (t, ~from: int, ~to_: int) => t = "substring" @@ -866,6 +1040,10 @@ Js.String2.substringToEnd("playground", ~from=-3) == "playground" Js.String2.substringToEnd("playground", ~from=12) == "" ``` */ +@deprecated({ + reason: "Use `String.substringToEnd` instead", + migrate: String.substringToEnd(~start=%insert.labelledArgument("from")), +}) @send external substringToEnd: (t, ~from: int) => t = "substring" @@ -887,6 +1065,10 @@ Js.String2.toLowerCase(`ΣΠ`) == `σπ` Js.String2.toLowerCase(`ΠΣ`) == `πς` ``` */ +@deprecated({ + reason: "Use `String.toLowerCase` instead", + migrate: String.toLowerCase(), +}) @send external toLowerCase: t => t = "toLowerCase" @@ -895,6 +1077,10 @@ external toLowerCase: t => t = "toLowerCase" See [`String.toLocaleLowerCase`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase) on MDN. */ +@deprecated({ + reason: "Use `String.toLocaleLowerCase` instead", + migrate: String.toLocaleLowerCase(), +}) @send external toLocaleLowerCase: t => t = "toLocaleLowerCase" @@ -915,6 +1101,10 @@ Js.String2.toUpperCase(`Straße`) == `STRASSE` Js.String2.toUpperCase(`πς`) == `ΠΣ` ``` */ +@deprecated({ + reason: "Use `String.toUpperCase` instead", + migrate: String.toUpperCase(), +}) @send external toUpperCase: t => t = "toUpperCase" @@ -923,6 +1113,10 @@ external toUpperCase: t => t = "toUpperCase" See [`String.to:LocaleUpperCase`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase) on MDN. */ +@deprecated({ + reason: "Use `String.toLocaleUpperCase` instead", + migrate: String.toLocaleUpperCase(), +}) @send external toLocaleUpperCase: t => t = "toLocaleUpperCase" @@ -940,6 +1134,10 @@ Js.String2.trim(" abc def ") == "abc def" Js.String2.trim("\n\r\t abc def \n\n\t\r ") == "abc def" ``` */ +@deprecated({ + reason: "Use `String.trim` instead", + migrate: String.trim(), +}) @send external trim: t => t = "trim" @@ -959,7 +1157,7 @@ on MDN. Js.String2.anchor("Page One", "page1") == "Page One" ``` */ -@send +@deprecated("This function has been removed from the relevant web standards.") @send external anchor: (t, t) => t = "anchor" /** @@ -975,7 +1173,7 @@ on MDN. Js.String2.link("Go to page two", "page2.html") == "Go to page two" ``` */ -@send +@deprecated("This function has been removed from the relevant web standards.") @send external link: (t, t) => t = "link" /* FIXME: we should not encourage people to use [%identity], better @@ -994,4 +1192,8 @@ let arr = Js.Array2.fromMap(Js.String2.castToArrayLike(s), x => x) arr == ["a", "b", "c", "d", "e"] ``` */ +@deprecated({ + reason: "Use `Array.fromString` instead", + migrate: Array.fromString(), +}) external castToArrayLike: t => Js_array2.array_like = "%identity" diff --git a/runtime/Stdlib_Array.res b/runtime/Stdlib_Array.res index 32a9979b8f..c5e40bedf8 100644 --- a/runtime/Stdlib_Array.res +++ b/runtime/Stdlib_Array.res @@ -13,6 +13,8 @@ external unsafe_get: (array<'a>, int) => 'a = "%array_unsafe_get" @val external fromArrayLikeWithMap: (arrayLike<'a>, 'a => 'b) => array<'b> = "Array.from" +@val external fromString: string => array = "Array.from" + @send external fillAll: (array<'a>, 'a) => unit = "fill" @send external fillToEnd: (array<'a>, 'a, ~start: int) => unit = "fill" diff --git a/runtime/Stdlib_Array.resi b/runtime/Stdlib_Array.resi index c8f19770b7..825c65eea8 100644 --- a/runtime/Stdlib_Array.resi +++ b/runtime/Stdlib_Array.resi @@ -31,6 +31,17 @@ external fromIterator: Stdlib_Iterator.t<'a> => array<'a> = "Array.from" @val external fromArrayLikeWithMap: (arrayLike<'a>, 'a => 'b) => array<'b> = "Array.from" +/** +`fromString(str)` creates an array of each character as a separate string from the provided `str`. + +## Examples + +```rescript +Array.fromString("abcde") == ["a", "b", "c", "d", "e"] +``` +*/ +@val external fromString: string => array = "Array.from" + /** `make(~length, init)` creates an array of length `length` initialized with the value of `init`. diff --git a/runtime/Stdlib_String.resi b/runtime/Stdlib_String.resi index 896bf1dc4b..0d0b472d9b 100644 --- a/runtime/Stdlib_String.resi +++ b/runtime/Stdlib_String.resi @@ -1070,7 +1070,7 @@ String.trimStart(" Hello world! ") == "Hello world! " external trimStart: string => string = "trimStart" /** -`trinEnd(str)` returns a string that is `str` with whitespace stripped from the +`trimEnd(str)` returns a string that is `str` with whitespace stripped from the end of a string. Internal whitespace is not removed. See [`String.trimEnd`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) on MDN. diff --git a/tests/tools_tests/src/expected/StdlibMigrationNoCompile_String.res.expected b/tests/tools_tests/src/expected/StdlibMigrationNoCompile_String.res.expected new file mode 100644 index 0000000000..c99e65e613 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigrationNoCompile_String.res.expected @@ -0,0 +1,12 @@ +let normalizeByForm1 = "abcde"->String.normalizeByForm("a") +let normalizeByForm2 = String.normalizeByForm("abcde", "a") + +let unsafeReplaceBy01 = "abcde"->String.replaceRegExpBy0Unsafe(/d/, (_, _, _) => "f") +let unsafeReplaceBy02 = String.replaceRegExpBy0Unsafe("abcde", /d/, (_, _, _) => "f") + +let unsafeReplaceBy11 = "abcde"->String.replaceRegExpBy1Unsafe(/d/, (_, _, _, _) => "f") +let unsafeReplaceBy12 = String.replaceRegExpBy1Unsafe("abcde", /d/, (_, _, _, _) => "f") + +let unsafeReplaceBy21 = "abcde"->String.replaceRegExpBy2Unsafe(/d/, (_, _, _, _, _) => "f") +let unsafeReplaceBy22 = String.replaceRegExpBy2Unsafe("abcde", /d/, (_, _, _, _, _) => "f") + diff --git a/tests/tools_tests/src/expected/StdlibMigration_String.res.expected b/tests/tools_tests/src/expected/StdlibMigration_String.res.expected new file mode 100644 index 0000000000..031edd3828 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_String.res.expected @@ -0,0 +1,126 @@ +let make1 = 1->String.make +let make2 = String.make(1) + +let fromCharCode1 = 65->String.fromCharCode +let fromCharCode2 = String.fromCharCode(65) + +let fromCharCodeMany1 = [65, 66, 67]->String.fromCharCodeMany +let fromCharCodeMany2 = String.fromCharCodeMany([65, 66, 67]) + +let fromCodePoint1 = 65->String.fromCodePoint +let fromCodePoint2 = String.fromCodePoint(65) + +let fromCodePointMany1 = [65, 66, 67]->String.fromCodePointMany +let fromCodePointMany2 = String.fromCodePointMany([65, 66, 67]) + +let length1 = "abcde"->String.length +let length2 = String.length("abcde") + +let get1 = "abcde"->String.getUnsafe(2) +let get2 = String.getUnsafe("abcde", 2) + +let charAt1 = "abcde"->String.charAt(2) +let charAt2 = String.charAt("abcde", 2) + +let charCodeAt1 = "abcde"->String.charCodeAt(2) +let charCodeAt2 = String.charCodeAt("abcde", 2) + +let codePointAt1 = "abcde"->String.codePointAt(2) +let codePointAt2 = String.codePointAt("abcde", 2) + +let concat1 = "abcde"->String.concat("fghij") +let concat2 = String.concat("abcde", "fghij") + +let concatMany1 = "abcde"->String.concatMany(["fghij", "klmno"]) +let concatMany2 = String.concatMany("abcde", ["fghij", "klmno"]) + +let endsWith1 = "abcde"->String.endsWith("de") +let endsWith2 = String.endsWith("abcde", "de") + +let endsWithFrom1 = "abcde"->String.endsWithFrom("d", 2) +let endsWithFrom2 = String.endsWithFrom("abcde", "d", 2) + +let includes1 = "abcde"->String.includes("de") +let includes2 = String.includes("abcde", "de") + +let includesFrom1 = "abcde"->String.includesFrom("d", 2) +let includesFrom2 = String.includesFrom("abcde", "d", 2) + +let indexOf1 = "abcde"->String.indexOf("de") +let indexOf2 = String.indexOf("abcde", "de") + +let indexOfFrom1 = "abcde"->String.indexOfFrom("d", 2) +let indexOfFrom2 = String.indexOfFrom("abcde", "d", 2) + +let lastIndexOf1 = "abcde"->String.lastIndexOf("de") +let lastIndexOf2 = String.lastIndexOf("abcde", "de") + +let lastIndexOfFrom1 = "abcde"->String.lastIndexOfFrom("d", 2) +let lastIndexOfFrom2 = String.lastIndexOfFrom("abcde", "d", 2) + +let localeCompare1 = "abcde"->String.localeCompare("fghij") +let localeCompare2 = String.localeCompare("abcde", "fghij") + +let match1 = "abcde"->String.match(/d/) +let match2 = String.match("abcde", /d/) + +let normalize1 = "abcde"->String.normalize +let normalize2 = String.normalize("abcde") + +let repeat1 = "abcde"->String.repeat(2) +let repeat2 = String.repeat("abcde", 2) + +let replace1 = "abcde"->String.replace("d", "f") +let replace2 = String.replace("abcde", "d", "f") + +let replaceByRe1 = "abcde"->String.replaceRegExp(/d/, "f") +let replaceByRe2 = String.replaceRegExp("abcde", /d/, "f") + +let search1 = "abcde"->String.search(/d/) +let search2 = String.search("abcde", /d/) + +let slice1 = "abcde"->String.slice(~start=1, ~end=3) +let slice2 = String.slice("abcde", ~start=1, ~end=3) + +let sliceToEnd1 = "abcde"->String.sliceToEnd(~start=1) +let sliceToEnd2 = String.sliceToEnd("abcde", ~start=1) + +let split1 = "abcde"->String.split("d") +let split2 = String.split("abcde", "d") + +let splitAtMost1 = "abcde"->String.splitAtMost("d", ~limit=2) +let splitAtMost2 = String.splitAtMost("abcde", "d", ~limit=2) + +let splitByRe1 = "abcde"->String.splitByRegExp(/d/) +let splitByRe2 = String.splitByRegExp("abcde", /d/) + +let splitByReAtMost1 = "abcde"->String.splitByRegExpAtMost(/d/, ~limit=2) +let splitByReAtMost2 = String.splitByRegExpAtMost("abcde", /d/, ~limit=2) + +let startsWith1 = "abcde"->String.startsWith("ab") +let startsWith2 = String.startsWith("abcde", "ab") + +let startsWithFrom1 = "abcde"->String.startsWithFrom("b", 1) +let startsWithFrom2 = String.startsWithFrom("abcde", "b", 1) + +let substring1 = "abcde"->String.substring(~start=1, ~end=3) +let substring2 = String.substring("abcde", ~start=1, ~end=3) + +let substringToEnd1 = "abcde"->String.substringToEnd(~start=1) +let substringToEnd2 = String.substringToEnd("abcde", ~start=1) + +let toLowerCase1 = "abcde"->String.toLowerCase +let toLowerCase2 = String.toLowerCase("abcde") + +let toLocaleLowerCase1 = "abcde"->String.toLocaleLowerCase +let toLocaleLowerCase2 = String.toLocaleLowerCase("abcde") + +let toUpperCase1 = "abcde"->String.toUpperCase +let toUpperCase2 = String.toUpperCase("abcde") + +let toLocaleUpperCase1 = "abcde"->String.toLocaleUpperCase +let toLocaleUpperCase2 = String.toLocaleUpperCase("abcde") + +let trim1 = "abcde"->String.trim +let trim2 = String.trim("abcde") + diff --git a/tests/tools_tests/src/migrate/StdlibMigrationNoCompile_String.res b/tests/tools_tests/src/migrate/StdlibMigrationNoCompile_String.res new file mode 100644 index 0000000000..d377bf2d05 --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigrationNoCompile_String.res @@ -0,0 +1,11 @@ +let normalizeByForm1 = "abcde"->Js.String2.normalizeByForm("a") +let normalizeByForm2 = Js.String2.normalizeByForm("abcde", "a") + +let unsafeReplaceBy01 = "abcde"->Js.String2.unsafeReplaceBy0(/d/, (_, _, _) => "f") +let unsafeReplaceBy02 = Js.String2.unsafeReplaceBy0("abcde", /d/, (_, _, _) => "f") + +let unsafeReplaceBy11 = "abcde"->Js.String2.unsafeReplaceBy1(/d/, (_, _, _, _) => "f") +let unsafeReplaceBy12 = Js.String2.unsafeReplaceBy1("abcde", /d/, (_, _, _, _) => "f") + +let unsafeReplaceBy21 = "abcde"->Js.String2.unsafeReplaceBy2(/d/, (_, _, _, _, _) => "f") +let unsafeReplaceBy22 = Js.String2.unsafeReplaceBy2("abcde", /d/, (_, _, _, _, _) => "f") diff --git a/tests/tools_tests/src/migrate/StdlibMigration_String.res b/tests/tools_tests/src/migrate/StdlibMigration_String.res new file mode 100644 index 0000000000..a67821e71f --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_String.res @@ -0,0 +1,125 @@ +let make1 = 1->Js.String2.make +let make2 = Js.String2.make(1) + +let fromCharCode1 = 65->Js.String2.fromCharCode +let fromCharCode2 = Js.String2.fromCharCode(65) + +let fromCharCodeMany1 = [65, 66, 67]->Js.String2.fromCharCodeMany +let fromCharCodeMany2 = Js.String2.fromCharCodeMany([65, 66, 67]) + +let fromCodePoint1 = 65->Js.String2.fromCodePoint +let fromCodePoint2 = Js.String2.fromCodePoint(65) + +let fromCodePointMany1 = [65, 66, 67]->Js.String2.fromCodePointMany +let fromCodePointMany2 = Js.String2.fromCodePointMany([65, 66, 67]) + +let length1 = "abcde"->Js.String2.length +let length2 = Js.String2.length("abcde") + +let get1 = "abcde"->Js.String2.get(2) +let get2 = Js.String2.get("abcde", 2) + +let charAt1 = "abcde"->Js.String2.charAt(2) +let charAt2 = Js.String2.charAt("abcde", 2) + +let charCodeAt1 = "abcde"->Js.String2.charCodeAt(2) +let charCodeAt2 = Js.String2.charCodeAt("abcde", 2) + +let codePointAt1 = "abcde"->Js.String2.codePointAt(2) +let codePointAt2 = Js.String2.codePointAt("abcde", 2) + +let concat1 = "abcde"->Js.String2.concat("fghij") +let concat2 = Js.String2.concat("abcde", "fghij") + +let concatMany1 = "abcde"->Js.String2.concatMany(["fghij", "klmno"]) +let concatMany2 = Js.String2.concatMany("abcde", ["fghij", "klmno"]) + +let endsWith1 = "abcde"->Js.String2.endsWith("de") +let endsWith2 = Js.String2.endsWith("abcde", "de") + +let endsWithFrom1 = "abcde"->Js.String2.endsWithFrom("d", 2) +let endsWithFrom2 = Js.String2.endsWithFrom("abcde", "d", 2) + +let includes1 = "abcde"->Js.String2.includes("de") +let includes2 = Js.String2.includes("abcde", "de") + +let includesFrom1 = "abcde"->Js.String2.includesFrom("d", 2) +let includesFrom2 = Js.String2.includesFrom("abcde", "d", 2) + +let indexOf1 = "abcde"->Js.String2.indexOf("de") +let indexOf2 = Js.String2.indexOf("abcde", "de") + +let indexOfFrom1 = "abcde"->Js.String2.indexOfFrom("d", 2) +let indexOfFrom2 = Js.String2.indexOfFrom("abcde", "d", 2) + +let lastIndexOf1 = "abcde"->Js.String2.lastIndexOf("de") +let lastIndexOf2 = Js.String2.lastIndexOf("abcde", "de") + +let lastIndexOfFrom1 = "abcde"->Js.String2.lastIndexOfFrom("d", 2) +let lastIndexOfFrom2 = Js.String2.lastIndexOfFrom("abcde", "d", 2) + +let localeCompare1 = "abcde"->Js.String2.localeCompare("fghij") +let localeCompare2 = Js.String2.localeCompare("abcde", "fghij") + +let match1 = "abcde"->Js.String2.match_(/d/) +let match2 = Js.String2.match_("abcde", /d/) + +let normalize1 = "abcde"->Js.String2.normalize +let normalize2 = Js.String2.normalize("abcde") + +let repeat1 = "abcde"->Js.String2.repeat(2) +let repeat2 = Js.String2.repeat("abcde", 2) + +let replace1 = "abcde"->Js.String2.replace("d", "f") +let replace2 = Js.String2.replace("abcde", "d", "f") + +let replaceByRe1 = "abcde"->Js.String2.replaceByRe(/d/, "f") +let replaceByRe2 = Js.String2.replaceByRe("abcde", /d/, "f") + +let search1 = "abcde"->Js.String2.search(/d/) +let search2 = Js.String2.search("abcde", /d/) + +let slice1 = "abcde"->Js.String2.slice(~from=1, ~to_=3) +let slice2 = Js.String2.slice("abcde", ~from=1, ~to_=3) + +let sliceToEnd1 = "abcde"->Js.String2.sliceToEnd(~from=1) +let sliceToEnd2 = Js.String2.sliceToEnd("abcde", ~from=1) + +let split1 = "abcde"->Js.String2.split("d") +let split2 = Js.String2.split("abcde", "d") + +let splitAtMost1 = "abcde"->Js.String2.splitAtMost("d", ~limit=2) +let splitAtMost2 = Js.String2.splitAtMost("abcde", "d", ~limit=2) + +let splitByRe1 = "abcde"->Js.String2.splitByRe(/d/) +let splitByRe2 = Js.String2.splitByRe("abcde", /d/) + +let splitByReAtMost1 = "abcde"->Js.String2.splitByReAtMost(/d/, ~limit=2) +let splitByReAtMost2 = Js.String2.splitByReAtMost("abcde", /d/, ~limit=2) + +let startsWith1 = "abcde"->Js.String2.startsWith("ab") +let startsWith2 = Js.String2.startsWith("abcde", "ab") + +let startsWithFrom1 = "abcde"->Js.String2.startsWithFrom("b", 1) +let startsWithFrom2 = Js.String2.startsWithFrom("abcde", "b", 1) + +let substring1 = "abcde"->Js.String2.substring(~from=1, ~to_=3) +let substring2 = Js.String2.substring("abcde", ~from=1, ~to_=3) + +let substringToEnd1 = "abcde"->Js.String2.substringToEnd(~from=1) +let substringToEnd2 = Js.String2.substringToEnd("abcde", ~from=1) + +let toLowerCase1 = "abcde"->Js.String2.toLowerCase +let toLowerCase2 = Js.String2.toLowerCase("abcde") + +let toLocaleLowerCase1 = "abcde"->Js.String2.toLocaleLowerCase +let toLocaleLowerCase2 = Js.String2.toLocaleLowerCase("abcde") + +let toUpperCase1 = "abcde"->Js.String2.toUpperCase +let toUpperCase2 = Js.String2.toUpperCase("abcde") + +let toLocaleUpperCase1 = "abcde"->Js.String2.toLocaleUpperCase +let toLocaleUpperCase2 = Js.String2.toLocaleUpperCase("abcde") + +let trim1 = "abcde"->Js.String2.trim +let trim2 = Js.String2.trim("abcde") diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_String.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_String.res new file mode 100644 index 0000000000..9e67e14937 --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_String.res @@ -0,0 +1,127 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_String.res. +let make1 = 1->String.make +let make2 = String.make(1) + +let fromCharCode1 = 65->String.fromCharCode +let fromCharCode2 = String.fromCharCode(65) + +let fromCharCodeMany1 = [65, 66, 67]->String.fromCharCodeMany +let fromCharCodeMany2 = String.fromCharCodeMany([65, 66, 67]) + +let fromCodePoint1 = 65->String.fromCodePoint +let fromCodePoint2 = String.fromCodePoint(65) + +let fromCodePointMany1 = [65, 66, 67]->String.fromCodePointMany +let fromCodePointMany2 = String.fromCodePointMany([65, 66, 67]) + +let length1 = "abcde"->String.length +let length2 = String.length("abcde") + +let get1 = "abcde"->String.getUnsafe(2) +let get2 = String.getUnsafe("abcde", 2) + +let charAt1 = "abcde"->String.charAt(2) +let charAt2 = String.charAt("abcde", 2) + +let charCodeAt1 = "abcde"->String.charCodeAt(2) +let charCodeAt2 = String.charCodeAt("abcde", 2) + +let codePointAt1 = "abcde"->String.codePointAt(2) +let codePointAt2 = String.codePointAt("abcde", 2) + +let concat1 = "abcde"->String.concat("fghij") +let concat2 = String.concat("abcde", "fghij") + +let concatMany1 = "abcde"->String.concatMany(["fghij", "klmno"]) +let concatMany2 = String.concatMany("abcde", ["fghij", "klmno"]) + +let endsWith1 = "abcde"->String.endsWith("de") +let endsWith2 = String.endsWith("abcde", "de") + +let endsWithFrom1 = "abcde"->String.endsWithFrom("d", 2) +let endsWithFrom2 = String.endsWithFrom("abcde", "d", 2) + +let includes1 = "abcde"->String.includes("de") +let includes2 = String.includes("abcde", "de") + +let includesFrom1 = "abcde"->String.includesFrom("d", 2) +let includesFrom2 = String.includesFrom("abcde", "d", 2) + +let indexOf1 = "abcde"->String.indexOf("de") +let indexOf2 = String.indexOf("abcde", "de") + +let indexOfFrom1 = "abcde"->String.indexOfFrom("d", 2) +let indexOfFrom2 = String.indexOfFrom("abcde", "d", 2) + +let lastIndexOf1 = "abcde"->String.lastIndexOf("de") +let lastIndexOf2 = String.lastIndexOf("abcde", "de") + +let lastIndexOfFrom1 = "abcde"->String.lastIndexOfFrom("d", 2) +let lastIndexOfFrom2 = String.lastIndexOfFrom("abcde", "d", 2) + +let localeCompare1 = "abcde"->String.localeCompare("fghij") +let localeCompare2 = String.localeCompare("abcde", "fghij") + +let match1 = "abcde"->String.match(/d/) +let match2 = String.match("abcde", /d/) + +let normalize1 = "abcde"->String.normalize +let normalize2 = String.normalize("abcde") + +let repeat1 = "abcde"->String.repeat(2) +let repeat2 = String.repeat("abcde", 2) + +let replace1 = "abcde"->String.replace("d", "f") +let replace2 = String.replace("abcde", "d", "f") + +let replaceByRe1 = "abcde"->String.replaceRegExp(/d/, "f") +let replaceByRe2 = String.replaceRegExp("abcde", /d/, "f") + +let search1 = "abcde"->String.search(/d/) +let search2 = String.search("abcde", /d/) + +let slice1 = "abcde"->String.slice(~start=1, ~end=3) +let slice2 = String.slice("abcde", ~start=1, ~end=3) + +let sliceToEnd1 = "abcde"->String.sliceToEnd(~start=1) +let sliceToEnd2 = String.sliceToEnd("abcde", ~start=1) + +let split1 = "abcde"->String.split("d") +let split2 = String.split("abcde", "d") + +let splitAtMost1 = "abcde"->String.splitAtMost("d", ~limit=2) +let splitAtMost2 = String.splitAtMost("abcde", "d", ~limit=2) + +let splitByRe1 = "abcde"->String.splitByRegExp(/d/) +let splitByRe2 = String.splitByRegExp("abcde", /d/) + +let splitByReAtMost1 = "abcde"->String.splitByRegExpAtMost(/d/, ~limit=2) +let splitByReAtMost2 = String.splitByRegExpAtMost("abcde", /d/, ~limit=2) + +let startsWith1 = "abcde"->String.startsWith("ab") +let startsWith2 = String.startsWith("abcde", "ab") + +let startsWithFrom1 = "abcde"->String.startsWithFrom("b", 1) +let startsWithFrom2 = String.startsWithFrom("abcde", "b", 1) + +let substring1 = "abcde"->String.substring(~start=1, ~end=3) +let substring2 = String.substring("abcde", ~start=1, ~end=3) + +let substringToEnd1 = "abcde"->String.substringToEnd(~start=1) +let substringToEnd2 = String.substringToEnd("abcde", ~start=1) + +let toLowerCase1 = "abcde"->String.toLowerCase +let toLowerCase2 = String.toLowerCase("abcde") + +let toLocaleLowerCase1 = "abcde"->String.toLocaleLowerCase +let toLocaleLowerCase2 = String.toLocaleLowerCase("abcde") + +let toUpperCase1 = "abcde"->String.toUpperCase +let toUpperCase2 = String.toUpperCase("abcde") + +let toLocaleUpperCase1 = "abcde"->String.toLocaleUpperCase +let toLocaleUpperCase2 = String.toLocaleUpperCase("abcde") + +let trim1 = "abcde"->String.trim +let trim2 = String.trim("abcde") From ced42f1d38f97eae9169bbba0fedad439785171d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 18:55:16 +0200 Subject: [PATCH 31/43] add chained to show weirdness --- runtime/Js_json.resi | 7 ++ .../StdlibMigration_JSON.res.expected | 19 ++++ .../src/migrate/StdlibMigration_JSON.res | 11 +++ .../Migrated_StdlibMigration_JSON.res | 20 +++++ tools/src/tools.ml | 87 ++++++++++++++++++- 5 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigration_JSON.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_JSON.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_JSON.res diff --git a/runtime/Js_json.resi b/runtime/Js_json.resi index 4dce84f7ea..4b19884fd0 100644 --- a/runtime/Js_json.resi +++ b/runtime/Js_json.resi @@ -73,6 +73,13 @@ let test: ('a, Kind.t<'b>) => bool /** `decodeString(json)` returns `Some(s)` if `json` is a `string`, `None` otherwise. */ +@deprecated({ + reason: "Use pattern matching instead.", + migrate: switch %insert.unlabelledArgument(0) { + | JSON.String(str) => Some(str) + | _ => None + } +}) let decodeString: t => option /** diff --git a/tests/tools_tests/src/expected/StdlibMigration_JSON.res.expected b/tests/tools_tests/src/expected/StdlibMigration_JSON.res.expected new file mode 100644 index 0000000000..f5e4af5f9c --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_JSON.res.expected @@ -0,0 +1,19 @@ +external someJson: Js_json.t = "someJson" +external strToJson: string => Js_json.t = "strToJson" + +let decodeString1 = switch someJson { +| JSON.String(str) => Some(str) +| _ => None +} +let decodeString2 = switch someJson { +| JSON.String(str) => Some(str) +| _ => None +} +let decodeString3 = switch [1, 2, 3] +->Array.map(v => v->Int.toString) +->Array.join(" ") +->strToJson { +| JSON.String(str) => Some(str) +| _ => None +} + diff --git a/tests/tools_tests/src/migrate/StdlibMigration_JSON.res b/tests/tools_tests/src/migrate/StdlibMigration_JSON.res new file mode 100644 index 0000000000..60e13379a2 --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_JSON.res @@ -0,0 +1,11 @@ +external someJson: Js_json.t = "someJson" +external strToJson: string => Js_json.t = "strToJson" + +let decodeString1 = someJson->Js_json.decodeString +let decodeString2 = Js_json.decodeString(someJson) +let decodeString3 = + [1, 2, 3] + ->Array.map(v => v->Int.toString) + ->Array.join(" ") + ->strToJson + ->Js_json.decodeString diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_JSON.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_JSON.res new file mode 100644 index 0000000000..103bd0109c --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_JSON.res @@ -0,0 +1,20 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_JSON.res. +external someJson: Js_json.t = "someJson" +external strToJson: string => Js_json.t = "strToJson" + +let decodeString1 = switch someJson { +| JSON.String(str) => Some(str) +| _ => None +} +let decodeString2 = switch someJson { +| JSON.String(str) => Some(str) +| _ => None +} +let decodeString3 = switch [1, 2, 3] +->Array.map(v => v->Int.toString) +->Array.join(" ") +->strToJson { +| JSON.String(str) => Some(str) +| _ => None +} diff --git a/tools/src/tools.ml b/tools/src/tools.ml index beb3230033..97faecedfc 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1306,7 +1306,6 @@ module Migrate = struct (* TODO: - - Migrate unlabelled arguments (Array.reduceXxx) - Migrate type usage (Js.Array2.t -> array, etc) *) @@ -1487,6 +1486,80 @@ module Migrate = struct filtered_args @ template_args_to_insert end + (* Finds a specific argument in a list of arguments. *) + let find_arg args (find_this_arg : [`Labelled of string | `Unlabelled of int]) + = + let unlabelled_count = ref 0 in + args + |> List.find_map (fun (lbl, arg) -> + match (find_this_arg, lbl) with + | ( `Labelled arg_name, + (Asttypes.Labelled {txt = label} | Optional {txt = label}) ) + when label = arg_name -> + Some (lbl, arg) + | `Unlabelled count, Nolabel -> + let current_count = !unlabelled_count in + incr unlabelled_count; + if current_count = count then Some (lbl, arg) else None + | _, Nolabel -> + incr unlabelled_count; + None + | _ -> None) + + let replace_from_args_in_expr expr source_args = + let mapper = + { + Ast_mapper.default_mapper with + expr = + (fun mapper exp -> + match exp with + | { + pexp_desc = + Pexp_extension + ( {txt = "insert.labelledArgument"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_constant (Pconst_string (arg_name, _)); + }, + _ ); + }; + ] ); + } -> ( + match find_arg source_args (`Labelled arg_name) with + | Some (_, arg) -> arg + | None -> exp) + | { + pexp_desc = + Pexp_extension + ( {txt = "insert.unlabelledArgument"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_constant (Pconst_integer (count_str, _)); + }, + _ ); + }; + ] ); + } -> ( + match + find_arg source_args (`Unlabelled (int_of_string count_str)) + with + | Some (_, arg) -> arg + | None -> exp) + | _ -> Ast_mapper.default_mapper.expr mapper exp); + } + in + mapper.expr mapper expr + let makeMapper (deprecated_used : Cmt_utils.deprecated_used list) = (* Function calls *) let deprecated_function_calls = @@ -1560,6 +1633,12 @@ module Migrate = struct (* TODO: Here we could add strict and partial mode, to control if args are merged or not. *) match deprecated_info.migration_template with + | Some {pexp_desc = Pexp_match (e, cases)} -> + { + exp with + pexp_desc = + Pexp_match (replace_from_args_in_expr e source_args, cases); + } | Some { pexp_desc = @@ -1610,6 +1689,12 @@ module Migrate = struct Hashtbl.remove loc_to_deprecated_fn_call fn_loc; match deprecated_info.migration_template with + | Some {pexp_desc = Pexp_match (e, cases)} -> + { + exp with + pexp_desc = + Pexp_match (replace_from_args_in_expr e [lhs], cases); + } | Some { pexp_desc = From f65558edae9756b30d10e0a31fba84036493230d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 18:55:51 +0200 Subject: [PATCH 32/43] format and testoutput --- runtime/Js_json.resi | 6 +++--- tests/analysis_tests/tests/src/expected/Completion.res.txt | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/Js_json.resi b/runtime/Js_json.resi index 4b19884fd0..7e18bf6828 100644 --- a/runtime/Js_json.resi +++ b/runtime/Js_json.resi @@ -76,9 +76,9 @@ let test: ('a, Kind.t<'b>) => bool @deprecated({ reason: "Use pattern matching instead.", migrate: switch %insert.unlabelledArgument(0) { - | JSON.String(str) => Some(str) - | _ => None - } + | JSON.String(str) => Some(str) + | _ => None + }, }) let decodeString: t => option diff --git a/tests/analysis_tests/tests/src/expected/Completion.res.txt b/tests/analysis_tests/tests/src/expected/Completion.res.txt index e43ce4c40c..f1e8b81926 100644 --- a/tests/analysis_tests/tests/src/expected/Completion.res.txt +++ b/tests/analysis_tests/tests/src/expected/Completion.res.txt @@ -316,6 +316,12 @@ Path Array. "tags": [], "detail": "array<'a> => unit", "documentation": {"kind": "markdown", "value": "\n`reverse(array)` reverses the order of the items in `array`.\n\nBeware this will *mutate* the array.\n\nSee [`Array.reverse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) on MDN.\n\n## Examples\n\n```rescript\nlet someArray = [\"hi\", \"hello\"]\nsomeArray->Array.reverse\n\nsomeArray == [\"hello\", \"hi\"]\n```\n"} + }, { + "label": "fromString", + "kind": 12, + "tags": [], + "detail": "string => array", + "documentation": {"kind": "markdown", "value": "\n`fromString(str)` creates an array of each character as a separate string from the provided `str`.\n\n## Examples\n\n```rescript\nArray.fromString(\"abcde\") == [\"a\", \"b\", \"c\", \"d\", \"e\"]\n```\n"} }, { "label": "findLastIndexWithIndex", "kind": 12, From 344d30957eb2506ef87893314737472f116cd542 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 18:56:41 +0200 Subject: [PATCH 33/43] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1c4f5ae69..2cdc91a896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Add optional `message` argument to `Result.getOrThrow` and improve default error message. https://github.com/rescript-lang/rescript/pull/7630 - Add `RegExp.escape` binding. https://github.com/rescript-lang/rescript/pull/7695 +- Add `Array.fromString`. https://github.com/rescript-lang/rescript/pull/7693 #### :bug: Bug fix @@ -46,6 +47,7 @@ - Suggest related functions with the expected arity in errors when it makes sense. https://github.com/rescript-lang/rescript/pull/7712 - Improve error when a constructor expects an inline record. https://github.com/rescript-lang/rescript/pull/7713 - Remove `@meth` attribute. https://github.com/rescript-lang/rescript/pull/7684 + > > > > > > > 00815832f (changelog) #### :house: Internal From b684235802693b8fe3e9e7ee691cf7bc2e716345 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 19:09:41 +0200 Subject: [PATCH 34/43] update --- .../build_tests/super_errors/fixtures/todo_with_no_payload.res | 2 +- tests/build_tests/super_errors/fixtures/todo_with_payload.res | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/build_tests/super_errors/fixtures/todo_with_no_payload.res b/tests/build_tests/super_errors/fixtures/todo_with_no_payload.res index d7aa60688c..e6ef3d533f 100644 --- a/tests/build_tests/super_errors/fixtures/todo_with_no_payload.res +++ b/tests/build_tests/super_errors/fixtures/todo_with_no_payload.res @@ -2,4 +2,4 @@ let implementMeLater = (): string => %todo let x = implementMeLater() -Js.log(x->Js.String2.includes("x")) +Console.log(x->String.includes("x")) diff --git a/tests/build_tests/super_errors/fixtures/todo_with_payload.res b/tests/build_tests/super_errors/fixtures/todo_with_payload.res index a52d80d5c1..d13d745dc7 100644 --- a/tests/build_tests/super_errors/fixtures/todo_with_payload.res +++ b/tests/build_tests/super_errors/fixtures/todo_with_payload.res @@ -2,4 +2,4 @@ let implementMeLater = (): string => %todo("This should return a string eventual let x = implementMeLater() -Js.log(x->Js.String2.includes("x")) +Console.log(x->String.includes("x")) From bfe602784d8b3c9b57c66636ca37c8a4441e736d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 19:12:33 +0200 Subject: [PATCH 35/43] Js.log -> Console.log --- runtime/Js.res | 40 ++++++++++++++++--- .../expected/StdlibMigration_Js.res.expected | 6 +++ .../src/migrate/StdlibMigration_Js.res | 5 +++ .../migrated/Migrated_StdlibMigration_Js.res | 7 ++++ 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigration_Js.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_Js.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Js.res diff --git a/runtime/Js.res b/runtime/Js.res index 4bdfdf03d1..833462bac9 100644 --- a/runtime/Js.res +++ b/runtime/Js.res @@ -203,16 +203,46 @@ external undefined: undefined<'a> = "%undefined" external typeof: 'a => string = "%typeof" /** Equivalent to console.log any value. */ -@val @scope("console") +@deprecated({ + reason: "Use `Console.log` instead.", + migrate: Console.log(), +}) +@val +@scope("console") external log: 'a => unit = "log" -@val @scope("console") external log2: ('a, 'b) => unit = "log" -@val @scope("console") external log3: ('a, 'b, 'c) => unit = "log" +@deprecated({ + reason: "Use `Console.log2` instead.", + migrate: Console.log2(), +}) +@val +@scope("console") +external log2: ('a, 'b) => unit = "log" + +@deprecated({ + reason: "Use `Console.log3` instead.", + migrate: Console.log3(), +}) +@val +@scope("console") +external log3: ('a, 'b, 'c) => unit = "log" -@val @scope("console") external log4: ('a, 'b, 'c, 'd) => unit = "log" +@deprecated({ + reason: "Use `Console.log4` instead.", + migrate: Console.log4(), +}) +@val +@scope("console") +external log4: ('a, 'b, 'c, 'd) => unit = "log" /** A convenience function to console.log more than 4 arguments */ -@val @scope("console") @variadic +@deprecated({ + reason: "Use `Console.logMany` instead.", + migrate: Console.logMany(), +}) +@val +@scope("console") +@variadic external logMany: array<'a> => unit = "log" external eqNull: ('a, null<'a>) => bool = "%equal_null" diff --git a/tests/tools_tests/src/expected/StdlibMigration_Js.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Js.res.expected new file mode 100644 index 0000000000..e87cdd9b25 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_Js.res.expected @@ -0,0 +1,6 @@ +let consoleLog1 = Console.log("Hello") +let consoleLog2 = Console.log2("Hello", "World") +let consoleLog3 = Console.log3("Hello", "World", "!") +let consoleLog4 = Console.log4("Hello", "World", "!", "!") +let consoleLogMany = Console.logMany(["Hello", "World"]) + diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Js.res b/tests/tools_tests/src/migrate/StdlibMigration_Js.res new file mode 100644 index 0000000000..39deedf002 --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_Js.res @@ -0,0 +1,5 @@ +let consoleLog1 = Js.log("Hello") +let consoleLog2 = Js.log2("Hello", "World") +let consoleLog3 = Js.log3("Hello", "World", "!") +let consoleLog4 = Js.log4("Hello", "World", "!", "!") +let consoleLogMany = Js.logMany(["Hello", "World"]) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Js.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Js.res new file mode 100644 index 0000000000..87922b7521 --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Js.res @@ -0,0 +1,7 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_Js.res. +let consoleLog1 = Console.log("Hello") +let consoleLog2 = Console.log2("Hello", "World") +let consoleLog3 = Console.log3("Hello", "World", "!") +let consoleLog4 = Console.log4("Hello", "World", "!", "!") +let consoleLogMany = Console.logMany(["Hello", "World"]) From bfaf026391bdf906a805d20dbcc09456194316ce Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 19:20:11 +0200 Subject: [PATCH 36/43] deprecate Js_console --- runtime/Js_console.res | 114 ++++++++++++++++++ .../StdlibMigration_Console.res.expected | 29 +++++ .../src/migrate/StdlibMigration_Console.res | 28 +++++ .../Migrated_StdlibMigration_Console.res | 30 +++++ 4 files changed, 201 insertions(+) create mode 100644 tests/tools_tests/src/expected/StdlibMigration_Console.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_Console.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Console.res diff --git a/runtime/Js_console.res b/runtime/Js_console.res index 9e0eda90c7..b353c8c924 100644 --- a/runtime/Js_console.res +++ b/runtime/Js_console.res @@ -1,29 +1,143 @@ +@deprecated({ + reason: "Use `Console.log` instead.", + migrate: Console.log(), +}) @val @scope("console") external log: 'a => unit = "log" + +@deprecated({ + reason: "Use `Console.log2` instead.", + migrate: Console.log2(), +}) @val @scope("console") external log2: ('a, 'b) => unit = "log" + +@deprecated({ + reason: "Use `Console.log3` instead.", + migrate: Console.log3(), +}) @val @scope("console") external log3: ('a, 'b, 'c) => unit = "log" + +@deprecated({ + reason: "Use `Console.log4` instead.", + migrate: Console.log4(), +}) @val @scope("console") external log4: ('a, 'b, 'c, 'd) => unit = "log" + +@deprecated({ + reason: "Use `Console.logMany` instead.", + migrate: Console.logMany(), +}) @val @scope("console") @variadic external logMany: array<'a> => unit = "log" +@deprecated({ + reason: "Use `Console.info` instead.", + migrate: Console.info(), +}) @val @scope("console") external info: 'a => unit = "info" + +@deprecated({ + reason: "Use `Console.info2` instead.", + migrate: Console.info2(), +}) @val @scope("console") external info2: ('a, 'b) => unit = "info" + +@deprecated({ + reason: "Use `Console.info3` instead.", + migrate: Console.info3(), +}) @val @scope("console") external info3: ('a, 'b, 'c) => unit = "info" + +@deprecated({ + reason: "Use `Console.info4` instead.", + migrate: Console.info4(), +}) @val @scope("console") external info4: ('a, 'b, 'c, 'd) => unit = "info" + +@deprecated({ + reason: "Use `Console.infoMany` instead.", + migrate: Console.infoMany(), +}) @val @scope("console") @variadic external infoMany: array<'a> => unit = "info" +@deprecated({ + reason: "Use `Console.warn` instead.", + migrate: Console.warn(), +}) @val @scope("console") external warn: 'a => unit = "warn" + +@deprecated({ + reason: "Use `Console.warn2` instead.", + migrate: Console.warn2(), +}) @val @scope("console") external warn2: ('a, 'b) => unit = "warn" + +@deprecated({ + reason: "Use `Console.warn3` instead.", + migrate: Console.warn3(), +}) @val @scope("console") external warn3: ('a, 'b, 'c) => unit = "warn" + +@deprecated({ + reason: "Use `Console.warn4` instead.", + migrate: Console.warn4(), +}) @val @scope("console") external warn4: ('a, 'b, 'c, 'd) => unit = "warn" + +@deprecated({ + reason: "Use `Console.warnMany` instead.", + migrate: Console.warnMany(), +}) @val @scope("console") @variadic external warnMany: array<'a> => unit = "warn" +@deprecated({ + reason: "Use `Console.error` instead.", + migrate: Console.error(), +}) @val @scope("console") external error: 'a => unit = "error" + +@deprecated({ + reason: "Use `Console.error2` instead.", + migrate: Console.error2(), +}) @val @scope("console") external error2: ('a, 'b) => unit = "error" + +@deprecated({ + reason: "Use `Console.error3` instead.", + migrate: Console.error3(), +}) @val @scope("console") external error3: ('a, 'b, 'c) => unit = "error" + +@deprecated({ + reason: "Use `Console.error4` instead.", + migrate: Console.error4(), +}) @val @scope("console") external error4: ('a, 'b, 'c, 'd) => unit = "error" + +@deprecated({ + reason: "Use `Console.errorMany` instead.", + migrate: Console.errorMany(), +}) @val @scope("console") @variadic external errorMany: array<'a> => unit = "error" +@deprecated({ + reason: "Use `Console.trace` instead.", + migrate: Console.trace(), +}) @val @scope("console") external trace: unit => unit = "trace" +@deprecated({ + reason: "Use `Console.time` instead.", + migrate: Console.time(), +}) @val @scope("console") external timeStart: string => unit = "time" +@deprecated({ + reason: "Use `Console.timeEnd` instead.", + migrate: Console.timeEnd(), +}) @val @scope("console") external timeEnd: string => unit = "timeEnd" + +@deprecated({ + reason: "Use `Console.table` instead.", + migrate: Console.table(), +}) +@val @scope("console") external table: 'a => unit = "table" diff --git a/tests/tools_tests/src/expected/StdlibMigration_Console.res.expected b/tests/tools_tests/src/expected/StdlibMigration_Console.res.expected new file mode 100644 index 0000000000..472198684f --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_Console.res.expected @@ -0,0 +1,29 @@ +let log = Console.log("Hello, World!") +let log2 = Console.log2("Hello", "World") +let log3 = Console.log3("Hello", "World", "!") +let log4 = Console.log4("Hello", "World", "!", "!") +let logMany = Console.logMany(["Hello", "World"]) + +let info = Console.info("Hello, World!") +let info2 = Console.info2("Hello", "World") +let info3 = Console.info3("Hello", "World", "!") +let info4 = Console.info4("Hello", "World", "!", "!") +let infoMany = Console.infoMany(["Hello", "World"]) + +let warn = Console.warn("Hello, World!") +let warn2 = Console.warn2("Hello", "World") +let warn3 = Console.warn3("Hello", "World", "!") +let warn4 = Console.warn4("Hello", "World", "!", "!") +let warnMany = Console.warnMany(["Hello", "World"]) + +let error = Console.error("Hello, World!") +let error2 = Console.error2("Hello", "World") +let error3 = Console.error3("Hello", "World", "!") +let error4 = Console.error4("Hello", "World", "!", "!") +let errorMany = Console.errorMany(["Hello", "World"]) + +let trace = Console.trace() +let timeStart = Console.time("Hello, World!") +let timeEnd = Console.timeEnd("Hello, World!") +let table = Console.table(["Hello", "World"]) + diff --git a/tests/tools_tests/src/migrate/StdlibMigration_Console.res b/tests/tools_tests/src/migrate/StdlibMigration_Console.res new file mode 100644 index 0000000000..0e142a0575 --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_Console.res @@ -0,0 +1,28 @@ +let log = Js_console.log("Hello, World!") +let log2 = Js_console.log2("Hello", "World") +let log3 = Js_console.log3("Hello", "World", "!") +let log4 = Js_console.log4("Hello", "World", "!", "!") +let logMany = Js_console.logMany(["Hello", "World"]) + +let info = Js_console.info("Hello, World!") +let info2 = Js_console.info2("Hello", "World") +let info3 = Js_console.info3("Hello", "World", "!") +let info4 = Js_console.info4("Hello", "World", "!", "!") +let infoMany = Js_console.infoMany(["Hello", "World"]) + +let warn = Js_console.warn("Hello, World!") +let warn2 = Js_console.warn2("Hello", "World") +let warn3 = Js_console.warn3("Hello", "World", "!") +let warn4 = Js_console.warn4("Hello", "World", "!", "!") +let warnMany = Js_console.warnMany(["Hello", "World"]) + +let error = Js_console.error("Hello, World!") +let error2 = Js_console.error2("Hello", "World") +let error3 = Js_console.error3("Hello", "World", "!") +let error4 = Js_console.error4("Hello", "World", "!", "!") +let errorMany = Js_console.errorMany(["Hello", "World"]) + +let trace = Js_console.trace() +let timeStart = Js_console.timeStart("Hello, World!") +let timeEnd = Js_console.timeEnd("Hello, World!") +let table = Js_console.table(["Hello", "World"]) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Console.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Console.res new file mode 100644 index 0000000000..d539a85560 --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_Console.res @@ -0,0 +1,30 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_Console.res. +let log = Console.log("Hello, World!") +let log2 = Console.log2("Hello", "World") +let log3 = Console.log3("Hello", "World", "!") +let log4 = Console.log4("Hello", "World", "!", "!") +let logMany = Console.logMany(["Hello", "World"]) + +let info = Console.info("Hello, World!") +let info2 = Console.info2("Hello", "World") +let info3 = Console.info3("Hello", "World", "!") +let info4 = Console.info4("Hello", "World", "!", "!") +let infoMany = Console.infoMany(["Hello", "World"]) + +let warn = Console.warn("Hello, World!") +let warn2 = Console.warn2("Hello", "World") +let warn3 = Console.warn3("Hello", "World", "!") +let warn4 = Console.warn4("Hello", "World", "!", "!") +let warnMany = Console.warnMany(["Hello", "World"]) + +let error = Console.error("Hello, World!") +let error2 = Console.error2("Hello", "World") +let error3 = Console.error3("Hello", "World", "!") +let error4 = Console.error4("Hello", "World", "!", "!") +let errorMany = Console.errorMany(["Hello", "World"]) + +let trace = Console.trace() +let timeStart = Console.time("Hello, World!") +let timeEnd = Console.timeEnd("Hello, World!") +let table = Console.table(["Hello", "World"]) From 88f7ccb372d0ce9f776838aab4db97b4b27a10f4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 19:20:30 +0200 Subject: [PATCH 37/43] format --- runtime/Js_console.res | 100 +++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 24 deletions(-) diff --git a/runtime/Js_console.res b/runtime/Js_console.res index b353c8c924..594ff1bb61 100644 --- a/runtime/Js_console.res +++ b/runtime/Js_console.res @@ -2,142 +2,194 @@ reason: "Use `Console.log` instead.", migrate: Console.log(), }) -@val @scope("console") external log: 'a => unit = "log" +@val +@scope("console") +external log: 'a => unit = "log" @deprecated({ reason: "Use `Console.log2` instead.", migrate: Console.log2(), }) -@val @scope("console") external log2: ('a, 'b) => unit = "log" +@val +@scope("console") +external log2: ('a, 'b) => unit = "log" @deprecated({ reason: "Use `Console.log3` instead.", migrate: Console.log3(), }) -@val @scope("console") external log3: ('a, 'b, 'c) => unit = "log" +@val +@scope("console") +external log3: ('a, 'b, 'c) => unit = "log" @deprecated({ reason: "Use `Console.log4` instead.", migrate: Console.log4(), }) -@val @scope("console") external log4: ('a, 'b, 'c, 'd) => unit = "log" +@val +@scope("console") +external log4: ('a, 'b, 'c, 'd) => unit = "log" @deprecated({ reason: "Use `Console.logMany` instead.", migrate: Console.logMany(), }) -@val @scope("console") @variadic external logMany: array<'a> => unit = "log" +@val +@scope("console") +@variadic +external logMany: array<'a> => unit = "log" @deprecated({ reason: "Use `Console.info` instead.", migrate: Console.info(), }) -@val @scope("console") external info: 'a => unit = "info" +@val +@scope("console") +external info: 'a => unit = "info" @deprecated({ reason: "Use `Console.info2` instead.", migrate: Console.info2(), }) -@val @scope("console") external info2: ('a, 'b) => unit = "info" +@val +@scope("console") +external info2: ('a, 'b) => unit = "info" @deprecated({ reason: "Use `Console.info3` instead.", migrate: Console.info3(), }) -@val @scope("console") external info3: ('a, 'b, 'c) => unit = "info" +@val +@scope("console") +external info3: ('a, 'b, 'c) => unit = "info" @deprecated({ reason: "Use `Console.info4` instead.", migrate: Console.info4(), }) -@val @scope("console") external info4: ('a, 'b, 'c, 'd) => unit = "info" +@val +@scope("console") +external info4: ('a, 'b, 'c, 'd) => unit = "info" @deprecated({ reason: "Use `Console.infoMany` instead.", migrate: Console.infoMany(), }) -@val @scope("console") @variadic external infoMany: array<'a> => unit = "info" +@val +@scope("console") +@variadic +external infoMany: array<'a> => unit = "info" @deprecated({ reason: "Use `Console.warn` instead.", migrate: Console.warn(), }) -@val @scope("console") external warn: 'a => unit = "warn" +@val +@scope("console") +external warn: 'a => unit = "warn" @deprecated({ reason: "Use `Console.warn2` instead.", migrate: Console.warn2(), }) -@val @scope("console") external warn2: ('a, 'b) => unit = "warn" +@val +@scope("console") +external warn2: ('a, 'b) => unit = "warn" @deprecated({ reason: "Use `Console.warn3` instead.", migrate: Console.warn3(), }) -@val @scope("console") external warn3: ('a, 'b, 'c) => unit = "warn" +@val +@scope("console") +external warn3: ('a, 'b, 'c) => unit = "warn" @deprecated({ reason: "Use `Console.warn4` instead.", migrate: Console.warn4(), }) -@val @scope("console") external warn4: ('a, 'b, 'c, 'd) => unit = "warn" +@val +@scope("console") +external warn4: ('a, 'b, 'c, 'd) => unit = "warn" @deprecated({ reason: "Use `Console.warnMany` instead.", migrate: Console.warnMany(), }) -@val @scope("console") @variadic external warnMany: array<'a> => unit = "warn" +@val +@scope("console") +@variadic +external warnMany: array<'a> => unit = "warn" @deprecated({ reason: "Use `Console.error` instead.", migrate: Console.error(), }) -@val @scope("console") external error: 'a => unit = "error" +@val +@scope("console") +external error: 'a => unit = "error" @deprecated({ reason: "Use `Console.error2` instead.", migrate: Console.error2(), }) -@val @scope("console") external error2: ('a, 'b) => unit = "error" +@val +@scope("console") +external error2: ('a, 'b) => unit = "error" @deprecated({ reason: "Use `Console.error3` instead.", migrate: Console.error3(), }) -@val @scope("console") external error3: ('a, 'b, 'c) => unit = "error" +@val +@scope("console") +external error3: ('a, 'b, 'c) => unit = "error" @deprecated({ reason: "Use `Console.error4` instead.", migrate: Console.error4(), }) -@val @scope("console") external error4: ('a, 'b, 'c, 'd) => unit = "error" +@val +@scope("console") +external error4: ('a, 'b, 'c, 'd) => unit = "error" @deprecated({ reason: "Use `Console.errorMany` instead.", migrate: Console.errorMany(), }) -@val @scope("console") @variadic external errorMany: array<'a> => unit = "error" +@val +@scope("console") +@variadic +external errorMany: array<'a> => unit = "error" @deprecated({ reason: "Use `Console.trace` instead.", migrate: Console.trace(), }) -@val @scope("console") external trace: unit => unit = "trace" +@val +@scope("console") +external trace: unit => unit = "trace" @deprecated({ reason: "Use `Console.time` instead.", migrate: Console.time(), }) -@val @scope("console") external timeStart: string => unit = "time" +@val +@scope("console") +external timeStart: string => unit = "time" @deprecated({ reason: "Use `Console.timeEnd` instead.", migrate: Console.timeEnd(), }) -@val @scope("console") external timeEnd: string => unit = "timeEnd" +@val +@scope("console") +external timeEnd: string => unit = "timeEnd" @deprecated({ reason: "Use `Console.table` instead.", migrate: Console.table(), }) -@val @scope("console") external table: 'a => unit = "table" +@val +@scope("console") +external table: 'a => unit = "table" From 8456e50c1b873adbf4b4ce4083e01fdb5ab23174 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 24 Jul 2025 19:24:43 +0200 Subject: [PATCH 38/43] fix tests --- .../super_errors/expected/warnings4.res.expected | 2 +- .../super_errors/expected/warnings5.res.expected | 12 ++++++------ .../super_errors/fixtures/curry_in_uncurry.res | 2 +- .../polyvariant_constructors_mismatch_second.res | 4 ++-- .../build_tests/super_errors/fixtures/warnings4.res | 2 +- .../build_tests/super_errors/fixtures/warnings5.res | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/build_tests/super_errors/expected/warnings4.res.expected b/tests/build_tests/super_errors/expected/warnings4.res.expected index 0e45f5b371..9eec05e9d6 100644 --- a/tests/build_tests/super_errors/expected/warnings4.res.expected +++ b/tests/build_tests/super_errors/expected/warnings4.res.expected @@ -5,7 +5,7 @@ 9 │ @val external x: myType = "myVariable" 10 │ 11 │ switch x { - 12 │ | #first => Js.log("first") + 12 │ | #first => Console.log("first") 13 │ } 14 │ diff --git a/tests/build_tests/super_errors/expected/warnings5.res.expected b/tests/build_tests/super_errors/expected/warnings5.res.expected index 204f9f1608..23302e6c3f 100644 --- a/tests/build_tests/super_errors/expected/warnings5.res.expected +++ b/tests/build_tests/super_errors/expected/warnings5.res.expected @@ -4,7 +4,7 @@ 10 │ 11 │ switch y { - 12 │ | {otherValue: false} => Js.log("first") + 12 │ | {otherValue: false} => Console.log("first") 13 │ } 14 │ @@ -18,7 +18,7 @@ Either bind these labels explicitly or add ', _' to the pattern. 9 │ @val external y: someRecord = "otherVariable" 10 │ 11 │ switch y { - 12 │ | {otherValue: false} => Js.log("first") + 12 │ | {otherValue: false} => Console.log("first") 13 │ } 14 │ 15 │ switch y { @@ -32,7 +32,7 @@ Either bind these labels explicitly or add ', _' to the pattern. 14 │ 15 │ switch y { - 16 │ | {typ: WithPayload(true)} => Js.log("first") + 16 │ | {typ: WithPayload(true)} => Console.log("first") 17 │ } 18 │ @@ -46,7 +46,7 @@ Either bind these labels explicitly or add ', _' to the pattern. 13 │ } 14 │ 15 │ switch y { - 16 │ | {typ: WithPayload(true)} => Js.log("first") + 16 │ | {typ: WithPayload(true)} => Console.log("first") 17 │ } 18 │ 19 │ let arr = [1] @@ -62,7 +62,7 @@ Either bind these labels explicitly or add ', _' to the pattern. 19 │ let arr = [1] 20 │ 21 │ switch arr { - 22 │ | [] => Js.log("") + 22 │ | [] => Console.log("") 23 │ } 24 │ 25 │ switch arr { @@ -77,7 +77,7 @@ Either bind these labels explicitly or add ', _' to the pattern. 23 │ } 24 │ 25 │ switch arr { - 26 │ | [one] => Js.log(one) + 26 │ | [one] => Console.log(one) 27 │ } 28 │ 29 │ switch arr { diff --git a/tests/build_tests/super_errors/fixtures/curry_in_uncurry.res b/tests/build_tests/super_errors/fixtures/curry_in_uncurry.res index 0c0c3b67e1..92e656deef 100644 --- a/tests/build_tests/super_errors/fixtures/curry_in_uncurry.res +++ b/tests/build_tests/super_errors/fixtures/curry_in_uncurry.res @@ -1,3 +1,3 @@ let f = (a, b) => a + b -f(2, 2)->Js.log +f(2, 2)->Console.log diff --git a/tests/build_tests/super_errors/fixtures/polyvariant_constructors_mismatch_second.res b/tests/build_tests/super_errors/fixtures/polyvariant_constructors_mismatch_second.res index d8eace4719..11299b95c3 100644 --- a/tests/build_tests/super_errors/fixtures/polyvariant_constructors_mismatch_second.res +++ b/tests/build_tests/super_errors/fixtures/polyvariant_constructors_mismatch_second.res @@ -1,7 +1,7 @@ let handle = (ev: [#Click | #KeyDown]) => switch ev { - | #Click => Js.log("clicked") - | #KeyDown => Js.log("key down") + | #Click => Console.log("clicked") + | #KeyDown => Console.log("key down") } let _ = handle(#Resize) diff --git a/tests/build_tests/super_errors/fixtures/warnings4.res b/tests/build_tests/super_errors/fixtures/warnings4.res index 3c65caefe2..94de143aa1 100644 --- a/tests/build_tests/super_errors/fixtures/warnings4.res +++ b/tests/build_tests/super_errors/fixtures/warnings4.res @@ -9,5 +9,5 @@ type myType = [ @val external x: myType = "myVariable" switch x { -| #first => Js.log("first") +| #first => Console.log("first") } diff --git a/tests/build_tests/super_errors/fixtures/warnings5.res b/tests/build_tests/super_errors/fixtures/warnings5.res index 9e69c5076d..a3739d03b6 100644 --- a/tests/build_tests/super_errors/fixtures/warnings5.res +++ b/tests/build_tests/super_errors/fixtures/warnings5.res @@ -9,21 +9,21 @@ type someRecord = { @val external y: someRecord = "otherVariable" switch y { -| {otherValue: false} => Js.log("first") +| {otherValue: false} => Console.log("first") } switch y { -| {typ: WithPayload(true)} => Js.log("first") +| {typ: WithPayload(true)} => Console.log("first") } let arr = [1] switch arr { -| [] => Js.log("") +| [] => Console.log("") } switch arr { -| [one] => Js.log(one) +| [one] => Console.log(one) } switch arr { From 41619b9f0ffc4080c0a980a82b9ffd890889c8e6 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 26 Jul 2025 09:47:01 +0200 Subject: [PATCH 39/43] Js_bigint -> BigInt --- runtime/Js_bigint.res | 39 ++++++++++++++++ runtime/Stdlib_BigInt.resi | 42 ++++++++++++++---- .../StdlibMigration_BigInt.res.expected | 43 ++++++++++++++++++ .../src/migrate/StdlibMigration_BigInt.res | 42 ++++++++++++++++++ .../Migrated_StdlibMigration_BigInt.res | 44 +++++++++++++++++++ tools/src/tools.ml | 1 + 6 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected create mode 100644 tests/tools_tests/src/migrate/StdlibMigration_BigInt.res create mode 100644 tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res diff --git a/runtime/Js_bigint.res b/runtime/Js_bigint.res index e19daff867..c5863440a8 100644 --- a/runtime/Js_bigint.res +++ b/runtime/Js_bigint.res @@ -30,6 +30,10 @@ try { } ``` */ +@deprecated({ + reason: "Use `fromStringOrThrow` instead", + migrate: BigInt.fromStringOrThrow(), +}) @val external fromStringExn: string => bigint = "BigInt" @@ -44,13 +48,40 @@ external \"/": (bigint, bigint) => bigint = "%divbigint" external mod: (bigint, bigint) => bigint = "%modbigint" external \"**": (bigint, bigint) => bigint = "%powbigint" +@deprecated({ + reason: "Use `&` operator or `BigInt.bitwiseAnd` instead.", + migrate: %insert.unlabelledArgument(0) & %insert.unlabelledArgument(1), +}) external land: (bigint, bigint) => bigint = "%andbigint" + +@deprecated({ + reason: "Use `bitwiseOr` instead.", + migrate: BigInt.bitwiseOr(), +}) external lor: (bigint, bigint) => bigint = "%orbigint" + +@deprecated({ + reason: "Use `^` operator or `BigInt.bitwiseXor` instead.", + migrate: %insert.unlabelledArgument(0) ^ %insert.unlabelledArgument(1), +}) external lxor: (bigint, bigint) => bigint = "%xorbigint" +@deprecated({ + reason: "Use `~` operator or `BigInt.bitwiseNot` instead.", + migrate: BigInt.bitwiseNot(), +}) let lnot = x => lxor(x, -1n) +@deprecated({ + reason: "Use `<<` operator or `BigInt.shiftLeft` instead.", + migrate: %insert.unlabelledArgument(0) << %insert.unlabelledArgument(1), +}) external lsl: (bigint, bigint) => bigint = "%lslbigint" + +@deprecated({ + reason: "Use `>>` operator or `BigInt.shiftRight` instead.", + migrate: %insert.unlabelledArgument(0) >> %insert.unlabelledArgument(1), +}) external asr: (bigint, bigint) => bigint = "%asrbigint" /** @@ -64,6 +95,10 @@ See [`toString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referen Js.BigInt.toString(123n)->Js.log ``` */ +@deprecated({ + reason: "Use `BigInt.toString` instead.", + migrate: BigInt.toString(), +}) @send external toString: bigint => string = "toString" @@ -77,5 +112,9 @@ Returns a string with a language-sensitive representation of this BigInt value. Js.BigInt.toString(123n)->Js.log ``` */ +@deprecated({ + reason: "Use `BigInt.toLocaleString` instead.", + migrate: BigInt.toLocaleString(), +}) @send external toLocaleString: bigint => string = "toLocaleString" diff --git a/runtime/Stdlib_BigInt.resi b/runtime/Stdlib_BigInt.resi index 41d8421ad5..afb3f83e27 100644 --- a/runtime/Stdlib_BigInt.resi +++ b/runtime/Stdlib_BigInt.resi @@ -80,7 +80,11 @@ BigInt.fromString("invalid") == None */ let fromString: string => option -@deprecated("Use `fromStringOrThrow` instead") @val +@deprecated({ + reason: "Use `fromStringOrThrow` instead", + migrate: BigInt.fromStringOrThrow(), +}) +@val external fromStringExn: string => bigint = "BigInt" /** @@ -147,7 +151,11 @@ BigInt.toString(123n) == "123" @send external toString: (bigint, ~radix: int=?) => string = "toString" -@deprecated("Use `toString` with `~radix` instead") @send +@deprecated({ + reason: "Use `toString` with `~radix` instead", + migrate: BigInt.toString(), +}) +@send external toStringWithRadix: (bigint, ~radix: int) => string = "toString" /** @@ -339,7 +347,10 @@ external ignore: bigint => unit = "%ignore" BigInt.land(7n, 4n) == 4n ``` */ -@deprecated("Use `&` operator or `bitwiseAnd` instead.") +@deprecated({ + reason: "Use `&` operator or `bitwiseAnd` instead.", + migrate: %insert.unlabelledArgument(0) & %insert.unlabelledArgument(1), +}) external land: (bigint, bigint) => bigint = "%andbigint" /** @@ -353,7 +364,10 @@ external land: (bigint, bigint) => bigint = "%andbigint" BigInt.lor(7n, 4n) == 7n ``` */ -@deprecated("Use `bitwiseOr` instead.") +@deprecated({ + reason: "Use `bitwiseOr` instead.", + migrate: BigInt.bitwiseOr(), +}) external lor: (bigint, bigint) => bigint = "%orbigint" /** @@ -367,7 +381,10 @@ external lor: (bigint, bigint) => bigint = "%orbigint" BigInt.lxor(7n, 4n) == 3n ``` */ -@deprecated("Use `^` operator or `bitwiseXor` instead.") +@deprecated({ + reason: "Use `^` operator or `bitwiseXor` instead.", + migrate: %insert.unlabelledArgument(0) ^ %insert.unlabelledArgument(1), +}) external lxor: (bigint, bigint) => bigint = "%xorbigint" /** @@ -381,7 +398,10 @@ external lxor: (bigint, bigint) => bigint = "%xorbigint" BigInt.lnot(2n) == -3n ``` */ -@deprecated("Use `~` operator or `bitwiseNot` instead.") +@deprecated({ + reason: "Use `~` operator or `bitwiseNot` instead.", + migrate: ~%insert.unlabelledArgument(0), +}) external lnot: bigint => bigint = "%bitnot_bigint" /** @@ -395,7 +415,10 @@ external lnot: bigint => bigint = "%bitnot_bigint" BigInt.lsl(4n, 1n) == 8n ``` */ -@deprecated("Use `<<` operator or `shiftLeft` instead.") +@deprecated({ + reason: "Use `<<` operator or `shiftLeft` instead.", + migrate: %insert.unlabelledArgument(0) << %insert.unlabelledArgument(1), +}) external lsl: (bigint, bigint) => bigint = "%lslbigint" /** @@ -409,5 +432,8 @@ external lsl: (bigint, bigint) => bigint = "%lslbigint" BigInt.asr(8n, 1n) == 4n ``` */ -@deprecated("Use `>>` operator or `shiftRight` instead.") +@deprecated({ + reason: "Use `>>` operator or `shiftRight` instead.", + migrate: %insert.unlabelledArgument(0) >> %insert.unlabelledArgument(1), +}) external asr: (bigint, bigint) => bigint = "%asrbigint" diff --git a/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected b/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected new file mode 100644 index 0000000000..429a814874 --- /dev/null +++ b/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected @@ -0,0 +1,43 @@ +let fromStringExn1 = "123"->BigInt.fromStringOrThrow +let fromStringExn2 = BigInt.fromStringOrThrow("123") + +let land1 = 7n->\"&"(4n) +let land2 = 7n & 4n + +let lor1 = 7n->BigInt.bitwiseOr(4n) +let lor2 = BigInt.bitwiseOr(7n, 4n) + +let lxor1 = 7n->\"^"(4n) +let lxor2 = 7n ^ 4n + +let lnot1 = 2n->Js.BigInt.lnot +let lnot2 = Js.BigInt.lnot(2n) + +let lsl1 = 4n->\"<<"(1n) +let lsl2 = 4n << 1n + +let asr1 = 8n->\">>"(1n) +let asr2 = 8n >> 1n + +let toString1 = 123n->BigInt.toString +let toString2 = BigInt.toString(123n) + +let toLocaleString1 = 123n->BigInt.toLocaleString +let toLocaleString2 = BigInt.toLocaleString(123n) + +// From the stdlib module +let stdlib_fromStringExn1 = "123"->BigInt.fromStringOrThrow +let stdlib_fromStringExn2 = BigInt.fromStringOrThrow("123") + +let stdlib_land1 = 7n & 4n + +let stdlib_lor1 = BigInt.bitwiseOr(7n, 4n) + +let stdlib_lxor1 = 7n ^ 4n + +let stdlib_lnot1 = ~2n + +let stdlib_lsl1 = 4n << 1n + +let stdlib_asr1 = 8n >> 1n + diff --git a/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res b/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res new file mode 100644 index 0000000000..df355e679a --- /dev/null +++ b/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res @@ -0,0 +1,42 @@ +let fromStringExn1 = "123"->Js.BigInt.fromStringExn +let fromStringExn2 = Js.BigInt.fromStringExn("123") + +let land1 = 7n->Js.BigInt.land(4n) +let land2 = Js.BigInt.land(7n, 4n) + +let lor1 = 7n->Js.BigInt.lor(4n) +let lor2 = Js.BigInt.lor(7n, 4n) + +let lxor1 = 7n->Js.BigInt.lxor(4n) +let lxor2 = Js.BigInt.lxor(7n, 4n) + +let lnot1 = 2n->Js.BigInt.lnot +let lnot2 = Js.BigInt.lnot(2n) + +let lsl1 = 4n->Js.BigInt.lsl(1n) +let lsl2 = Js.BigInt.lsl(4n, 1n) + +let asr1 = 8n->Js.BigInt.asr(1n) +let asr2 = Js.BigInt.asr(8n, 1n) + +let toString1 = 123n->Js.BigInt.toString +let toString2 = Js.BigInt.toString(123n) + +let toLocaleString1 = 123n->Js.BigInt.toLocaleString +let toLocaleString2 = Js.BigInt.toLocaleString(123n) + +// From the stdlib module +let stdlib_fromStringExn1 = "123"->BigInt.fromStringExn +let stdlib_fromStringExn2 = BigInt.fromStringExn("123") + +let stdlib_land1 = BigInt.land(7n, 4n) + +let stdlib_lor1 = BigInt.lor(7n, 4n) + +let stdlib_lxor1 = BigInt.lxor(7n, 4n) + +let stdlib_lnot1 = BigInt.lnot(2n) + +let stdlib_lsl1 = BigInt.lsl(4n, 1n) + +let stdlib_asr1 = BigInt.asr(8n, 1n) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res new file mode 100644 index 0000000000..a3230b4742 --- /dev/null +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res @@ -0,0 +1,44 @@ +// This file is autogenerated so it can be type checked. +// It's the migrated version of src/migrate/StdlibMigration_BigInt.res. +let fromStringExn1 = "123"->BigInt.fromStringOrThrow +let fromStringExn2 = BigInt.fromStringOrThrow("123") + +let land1 = 7n->\"&"(4n) +let land2 = 7n & 4n + +let lor1 = 7n->BigInt.bitwiseOr(4n) +let lor2 = BigInt.bitwiseOr(7n, 4n) + +let lxor1 = 7n->\"^"(4n) +let lxor2 = 7n ^ 4n + +let lnot1 = 2n->Js.BigInt.lnot +let lnot2 = Js.BigInt.lnot(2n) + +let lsl1 = 4n->\"<<"(1n) +let lsl2 = 4n << 1n + +let asr1 = 8n->\">>"(1n) +let asr2 = 8n >> 1n + +let toString1 = 123n->BigInt.toString +let toString2 = BigInt.toString(123n) + +let toLocaleString1 = 123n->BigInt.toLocaleString +let toLocaleString2 = BigInt.toLocaleString(123n) + +// From the stdlib module +let stdlib_fromStringExn1 = "123"->BigInt.fromStringOrThrow +let stdlib_fromStringExn2 = BigInt.fromStringOrThrow("123") + +let stdlib_land1 = 7n & 4n + +let stdlib_lor1 = BigInt.bitwiseOr(7n, 4n) + +let stdlib_lxor1 = 7n ^ 4n + +let stdlib_lnot1 = ~2n + +let stdlib_lsl1 = 4n << 1n + +let stdlib_asr1 = 8n >> 1n diff --git a/tools/src/tools.ml b/tools/src/tools.ml index 97faecedfc..b29521d384 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1307,6 +1307,7 @@ module Migrate = struct (* TODO: - Migrate type usage (Js.Array2.t -> array, etc) + - Add "migratePipe" for specific migrations of pipe calls, like `BigInt.land` that can be `&` when not piped, but needs to be `BigInt.bitwiseAnd` when piped. *) module MapperUtils = struct From 1f175b2e79dabe53d18cf31da2987f51495a3176 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 26 Jul 2025 10:03:44 +0200 Subject: [PATCH 40/43] support explicit pipe migration templates --- compiler/ml/builtin_attributes.ml | 19 +++++++++++++++---- compiler/ml/cmt_format.ml | 4 ++-- compiler/ml/cmt_format.mli | 3 ++- compiler/ml/cmt_utils.ml | 11 +++++++++-- runtime/Stdlib_BigInt.resi | 5 +++++ .../StdlibMigration_BigInt.res.expected | 15 ++++++++++----- .../src/migrate/StdlibMigration_BigInt.res | 15 ++++++++++----- .../Migrated_StdlibMigration_BigInt.res | 15 ++++++++++----- tools/src/tools.ml | 14 ++++++++++++-- 9 files changed, 75 insertions(+), 26 deletions(-) diff --git a/compiler/ml/builtin_attributes.ml b/compiler/ml/builtin_attributes.ml index 1a7d871b05..8dea16c662 100644 --- a/compiler/ml/builtin_attributes.ml +++ b/compiler/ml/builtin_attributes.ml @@ -104,21 +104,32 @@ let rec deprecated_of_attrs_with_migrate = function Some migration_template | _ -> None) in + let migration_piped_template = + fields + |> List.find_map (fun field -> + match field with + | { + lid = {txt = Lident "migratePiped"}; + x = migration_piped_template; + } -> + Some migration_piped_template + | _ -> None) + in (* TODO: Validate and error if expected shape mismatches *) match reason with - | Some reason -> Some (reason, migration_template) + | Some reason -> Some (reason, migration_template, migration_piped_template) | None -> None) | ({txt = "ocaml.deprecated" | "deprecated"; _}, p) :: _ -> - Some (string_of_opt_payload p, None) + Some (string_of_opt_payload p, None, None) | _ :: tl -> deprecated_of_attrs_with_migrate tl let check_deprecated ?deprecated_context loc attrs s = match deprecated_of_attrs_with_migrate attrs with | None -> () - | Some (txt, migration_template) -> + | Some (txt, migration_template, migration_piped_template) -> !Cmt_utils.record_deprecated_used - ?deprecated_context loc txt migration_template; + ?deprecated_context ?migration_template ?migration_piped_template loc txt; Location.deprecated loc (cat s txt) let check_deprecated_inclusion ~def ~use loc attrs1 attrs2 s = diff --git a/compiler/ml/cmt_format.ml b/compiler/ml/cmt_format.ml index 6d11caee27..4b9fb2b054 100644 --- a/compiler/ml/cmt_format.ml +++ b/compiler/ml/cmt_format.ml @@ -166,8 +166,8 @@ let add_saved_type b = saved_types := b :: !saved_types let get_saved_types () = !saved_types let set_saved_types l = saved_types := l -let record_deprecated_used ?deprecated_context source_loc deprecated_text migration_template = - deprecated_used := {Cmt_utils.source_loc; deprecated_text; migration_template; context = deprecated_context} :: !deprecated_used +let record_deprecated_used ?deprecated_context ?migration_template ?migration_piped_template source_loc deprecated_text = + deprecated_used := {Cmt_utils.source_loc; deprecated_text; migration_template; migration_piped_template; context = deprecated_context} :: !deprecated_used let _ = Cmt_utils.record_deprecated_used := record_deprecated_used diff --git a/compiler/ml/cmt_format.mli b/compiler/ml/cmt_format.mli index 8a68782996..620be12e50 100644 --- a/compiler/ml/cmt_format.mli +++ b/compiler/ml/cmt_format.mli @@ -114,9 +114,10 @@ val record_value_dependency : val record_deprecated_used : ?deprecated_context:Cmt_utils.deprecated_used_context -> + ?migration_template:Parsetree.expression -> + ?migration_piped_template:Parsetree.expression -> Location.t -> string -> - Parsetree.expression option -> unit (* diff --git a/compiler/ml/cmt_utils.ml b/compiler/ml/cmt_utils.ml index 2320953b18..f06a268427 100644 --- a/compiler/ml/cmt_utils.ml +++ b/compiler/ml/cmt_utils.ml @@ -4,6 +4,7 @@ type deprecated_used = { source_loc: Location.t; deprecated_text: string; migration_template: Parsetree.expression option; + migration_piped_template: Parsetree.expression option; context: deprecated_used_context option; } @@ -11,9 +12,15 @@ type cmt_extra_info = {deprecated_used: deprecated_used list} let record_deprecated_used : (?deprecated_context:deprecated_used_context -> + ?migration_template:Parsetree.expression -> + ?migration_piped_template:Parsetree.expression -> Location.t -> string -> - Parsetree.expression option -> unit) ref = - ref (fun ?deprecated_context _ _ _ -> ignore deprecated_context) + ref + (fun + ?deprecated_context ?migration_template ?migration_piped_template _ _ -> + ignore deprecated_context; + ignore migration_template; + ignore migration_piped_template) diff --git a/runtime/Stdlib_BigInt.resi b/runtime/Stdlib_BigInt.resi index afb3f83e27..063ef917fd 100644 --- a/runtime/Stdlib_BigInt.resi +++ b/runtime/Stdlib_BigInt.resi @@ -350,6 +350,7 @@ BigInt.land(7n, 4n) == 4n @deprecated({ reason: "Use `&` operator or `bitwiseAnd` instead.", migrate: %insert.unlabelledArgument(0) & %insert.unlabelledArgument(1), + migratePiped: BigInt.bitwiseAnd(), }) external land: (bigint, bigint) => bigint = "%andbigint" @@ -384,6 +385,7 @@ BigInt.lxor(7n, 4n) == 3n @deprecated({ reason: "Use `^` operator or `bitwiseXor` instead.", migrate: %insert.unlabelledArgument(0) ^ %insert.unlabelledArgument(1), + migratePiped: BigInt.bitwiseXor(), }) external lxor: (bigint, bigint) => bigint = "%xorbigint" @@ -401,6 +403,7 @@ BigInt.lnot(2n) == -3n @deprecated({ reason: "Use `~` operator or `bitwiseNot` instead.", migrate: ~%insert.unlabelledArgument(0), + migratePiped: BigInt.bitwiseNot(), }) external lnot: bigint => bigint = "%bitnot_bigint" @@ -418,6 +421,7 @@ BigInt.lsl(4n, 1n) == 8n @deprecated({ reason: "Use `<<` operator or `shiftLeft` instead.", migrate: %insert.unlabelledArgument(0) << %insert.unlabelledArgument(1), + migratePiped: BigInt.shiftLeft(), }) external lsl: (bigint, bigint) => bigint = "%lslbigint" @@ -435,5 +439,6 @@ BigInt.asr(8n, 1n) == 4n @deprecated({ reason: "Use `>>` operator or `shiftRight` instead.", migrate: %insert.unlabelledArgument(0) >> %insert.unlabelledArgument(1), + migratePiped: BigInt.shiftRight(), }) external asr: (bigint, bigint) => bigint = "%asrbigint" diff --git a/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected b/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected index 429a814874..115e5c65c2 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected @@ -29,15 +29,20 @@ let toLocaleString2 = BigInt.toLocaleString(123n) let stdlib_fromStringExn1 = "123"->BigInt.fromStringOrThrow let stdlib_fromStringExn2 = BigInt.fromStringOrThrow("123") -let stdlib_land1 = 7n & 4n +let stdlib_land1 = 7n->BigInt.bitwiseAnd(4n) +let stdlib_land2 = 7n & 4n let stdlib_lor1 = BigInt.bitwiseOr(7n, 4n) -let stdlib_lxor1 = 7n ^ 4n +let stdlib_lxor1 = 7n->BigInt.bitwiseXor(4n) +let stdlib_lxor2 = 7n ^ 4n -let stdlib_lnot1 = ~2n +let stdlib_lnot1 = 2n->BigInt.bitwiseNot +let stdlib_lnot2 = ~2n -let stdlib_lsl1 = 4n << 1n +let stdlib_lsl1 = 4n->BigInt.shiftLeft(1n) +let stdlib_lsl2 = 4n << 1n -let stdlib_asr1 = 8n >> 1n +let stdlib_asr1 = 8n->BigInt.shiftRight(1n) +let stdlib_asr2 = 8n >> 1n diff --git a/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res b/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res index df355e679a..219f3dd56f 100644 --- a/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res +++ b/tests/tools_tests/src/migrate/StdlibMigration_BigInt.res @@ -29,14 +29,19 @@ let toLocaleString2 = Js.BigInt.toLocaleString(123n) let stdlib_fromStringExn1 = "123"->BigInt.fromStringExn let stdlib_fromStringExn2 = BigInt.fromStringExn("123") -let stdlib_land1 = BigInt.land(7n, 4n) +let stdlib_land1 = 7n->BigInt.land(4n) +let stdlib_land2 = BigInt.land(7n, 4n) let stdlib_lor1 = BigInt.lor(7n, 4n) -let stdlib_lxor1 = BigInt.lxor(7n, 4n) +let stdlib_lxor1 = 7n->BigInt.lxor(4n) +let stdlib_lxor2 = BigInt.lxor(7n, 4n) -let stdlib_lnot1 = BigInt.lnot(2n) +let stdlib_lnot1 = 2n->BigInt.lnot +let stdlib_lnot2 = BigInt.lnot(2n) -let stdlib_lsl1 = BigInt.lsl(4n, 1n) +let stdlib_lsl1 = 4n->BigInt.lsl(1n) +let stdlib_lsl2 = BigInt.lsl(4n, 1n) -let stdlib_asr1 = BigInt.asr(8n, 1n) +let stdlib_asr1 = 8n->BigInt.asr(1n) +let stdlib_asr2 = BigInt.asr(8n, 1n) diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res index a3230b4742..62a32b0846 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res @@ -31,14 +31,19 @@ let toLocaleString2 = BigInt.toLocaleString(123n) let stdlib_fromStringExn1 = "123"->BigInt.fromStringOrThrow let stdlib_fromStringExn2 = BigInt.fromStringOrThrow("123") -let stdlib_land1 = 7n & 4n +let stdlib_land1 = 7n->BigInt.bitwiseAnd(4n) +let stdlib_land2 = 7n & 4n let stdlib_lor1 = BigInt.bitwiseOr(7n, 4n) -let stdlib_lxor1 = 7n ^ 4n +let stdlib_lxor1 = 7n->BigInt.bitwiseXor(4n) +let stdlib_lxor2 = 7n ^ 4n -let stdlib_lnot1 = ~2n +let stdlib_lnot1 = 2n->BigInt.bitwiseNot +let stdlib_lnot2 = ~2n -let stdlib_lsl1 = 4n << 1n +let stdlib_lsl1 = 4n->BigInt.shiftLeft(1n) +let stdlib_lsl2 = 4n << 1n -let stdlib_asr1 = 8n >> 1n +let stdlib_asr1 = 8n->BigInt.shiftRight(1n) +let stdlib_asr2 = 8n >> 1n diff --git a/tools/src/tools.ml b/tools/src/tools.ml index b29521d384..aafdaba331 100644 --- a/tools/src/tools.ml +++ b/tools/src/tools.ml @@ -1689,7 +1689,12 @@ module Migrate = struct in Hashtbl.remove loc_to_deprecated_fn_call fn_loc; - match deprecated_info.migration_template with + let migration_template_to_use = + match deprecated_info.migration_piped_template with + | Some template -> Some template + | None -> deprecated_info.migration_template + in + match migration_template_to_use with | Some {pexp_desc = Pexp_match (e, cases)} -> { exp with @@ -1768,7 +1773,12 @@ module Migrate = struct in Hashtbl.remove loc_to_deprecated_fn_call fn_loc; - match deprecated_info.migration_template with + let migration_template_to_use = + match deprecated_info.migration_piped_template with + | Some template -> Some template + | None -> deprecated_info.migration_template + in + match migration_template_to_use with | Some { pexp_desc = From 20173de546238be9f98e36f8d070193c5e8dfffe Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 26 Jul 2025 10:10:53 +0200 Subject: [PATCH 41/43] fix remaining deprecations for Js_bigint --- runtime/Js_bigint.res | 7 ++++++- .../src/expected/StdlibMigration_BigInt.res.expected | 8 ++++---- .../migrate/migrated/Migrated_StdlibMigration_BigInt.res | 8 ++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/runtime/Js_bigint.res b/runtime/Js_bigint.res index c5863440a8..2440c29e29 100644 --- a/runtime/Js_bigint.res +++ b/runtime/Js_bigint.res @@ -51,6 +51,7 @@ external \"**": (bigint, bigint) => bigint = "%powbigint" @deprecated({ reason: "Use `&` operator or `BigInt.bitwiseAnd` instead.", migrate: %insert.unlabelledArgument(0) & %insert.unlabelledArgument(1), + migratePiped: BigInt.bitwiseAnd(), }) external land: (bigint, bigint) => bigint = "%andbigint" @@ -63,24 +64,28 @@ external lor: (bigint, bigint) => bigint = "%orbigint" @deprecated({ reason: "Use `^` operator or `BigInt.bitwiseXor` instead.", migrate: %insert.unlabelledArgument(0) ^ %insert.unlabelledArgument(1), + migratePiped: BigInt.bitwiseXor(), }) external lxor: (bigint, bigint) => bigint = "%xorbigint" @deprecated({ reason: "Use `~` operator or `BigInt.bitwiseNot` instead.", - migrate: BigInt.bitwiseNot(), + migrate: ~%insert.unlabelledArgument(0), + migratePiped: BigInt.bitwiseNot(), }) let lnot = x => lxor(x, -1n) @deprecated({ reason: "Use `<<` operator or `BigInt.shiftLeft` instead.", migrate: %insert.unlabelledArgument(0) << %insert.unlabelledArgument(1), + migratePiped: BigInt.shiftLeft(), }) external lsl: (bigint, bigint) => bigint = "%lslbigint" @deprecated({ reason: "Use `>>` operator or `BigInt.shiftRight` instead.", migrate: %insert.unlabelledArgument(0) >> %insert.unlabelledArgument(1), + migratePiped: BigInt.shiftRight(), }) external asr: (bigint, bigint) => bigint = "%asrbigint" diff --git a/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected b/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected index 115e5c65c2..e674d8aee7 100644 --- a/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected +++ b/tests/tools_tests/src/expected/StdlibMigration_BigInt.res.expected @@ -1,22 +1,22 @@ let fromStringExn1 = "123"->BigInt.fromStringOrThrow let fromStringExn2 = BigInt.fromStringOrThrow("123") -let land1 = 7n->\"&"(4n) +let land1 = 7n->BigInt.bitwiseAnd(4n) let land2 = 7n & 4n let lor1 = 7n->BigInt.bitwiseOr(4n) let lor2 = BigInt.bitwiseOr(7n, 4n) -let lxor1 = 7n->\"^"(4n) +let lxor1 = 7n->BigInt.bitwiseXor(4n) let lxor2 = 7n ^ 4n let lnot1 = 2n->Js.BigInt.lnot let lnot2 = Js.BigInt.lnot(2n) -let lsl1 = 4n->\"<<"(1n) +let lsl1 = 4n->BigInt.shiftLeft(1n) let lsl2 = 4n << 1n -let asr1 = 8n->\">>"(1n) +let asr1 = 8n->BigInt.shiftRight(1n) let asr2 = 8n >> 1n let toString1 = 123n->BigInt.toString diff --git a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res index 62a32b0846..3ea3562f5d 100644 --- a/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res +++ b/tests/tools_tests/src/migrate/migrated/Migrated_StdlibMigration_BigInt.res @@ -3,22 +3,22 @@ let fromStringExn1 = "123"->BigInt.fromStringOrThrow let fromStringExn2 = BigInt.fromStringOrThrow("123") -let land1 = 7n->\"&"(4n) +let land1 = 7n->BigInt.bitwiseAnd(4n) let land2 = 7n & 4n let lor1 = 7n->BigInt.bitwiseOr(4n) let lor2 = BigInt.bitwiseOr(7n, 4n) -let lxor1 = 7n->\"^"(4n) +let lxor1 = 7n->BigInt.bitwiseXor(4n) let lxor2 = 7n ^ 4n let lnot1 = 2n->Js.BigInt.lnot let lnot2 = Js.BigInt.lnot(2n) -let lsl1 = 4n->\"<<"(1n) +let lsl1 = 4n->BigInt.shiftLeft(1n) let lsl2 = 4n << 1n -let asr1 = 8n->\">>"(1n) +let asr1 = 8n->BigInt.shiftRight(1n) let asr2 = 8n >> 1n let toString1 = 123n->BigInt.toString From 97c5da009309e53f1b1b60e7af265e842d887894 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 26 Jul 2025 10:11:18 +0200 Subject: [PATCH 42/43] format --- runtime/Js_bigint.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/Js_bigint.res b/runtime/Js_bigint.res index 2440c29e29..4695f8128b 100644 --- a/runtime/Js_bigint.res +++ b/runtime/Js_bigint.res @@ -70,7 +70,7 @@ external lxor: (bigint, bigint) => bigint = "%xorbigint" @deprecated({ reason: "Use `~` operator or `BigInt.bitwiseNot` instead.", - migrate: ~%insert.unlabelledArgument(0), + migrate: ~(%insert.unlabelledArgument(0)), migratePiped: BigInt.bitwiseNot(), }) let lnot = x => lxor(x, -1n) From 0bb54a7407562b8fd84347944100d751ff2e068f Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sat, 26 Jul 2025 14:21:24 +0200 Subject: [PATCH 43/43] format --- runtime/Stdlib_BigInt.resi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/Stdlib_BigInt.resi b/runtime/Stdlib_BigInt.resi index 063ef917fd..690307f96b 100644 --- a/runtime/Stdlib_BigInt.resi +++ b/runtime/Stdlib_BigInt.resi @@ -402,7 +402,7 @@ BigInt.lnot(2n) == -3n */ @deprecated({ reason: "Use `~` operator or `bitwiseNot` instead.", - migrate: ~%insert.unlabelledArgument(0), + migrate: ~(%insert.unlabelledArgument(0)), migratePiped: BigInt.bitwiseNot(), }) external lnot: bigint => bigint = "%bitnot_bigint"