From 531f89c449a946cc060561d4373414a13f7a230f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Mon, 5 May 2025 18:40:56 +0100 Subject: [PATCH 1/4] Rename ounit_json_tests to ounit_ext_json_tests --- .../{ounit_json_tests.ml => ounit_ext_json_tests.ml} | 0 tests/ounit_tests/ounit_tests_main.ml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/ounit_tests/{ounit_json_tests.ml => ounit_ext_json_tests.ml} (100%) diff --git a/tests/ounit_tests/ounit_json_tests.ml b/tests/ounit_tests/ounit_ext_json_tests.ml similarity index 100% rename from tests/ounit_tests/ounit_json_tests.ml rename to tests/ounit_tests/ounit_ext_json_tests.ml diff --git a/tests/ounit_tests/ounit_tests_main.ml b/tests/ounit_tests/ounit_tests_main.ml index 4c01311bb9..cc0dee6302 100644 --- a/tests/ounit_tests/ounit_tests_main.ml +++ b/tests/ounit_tests/ounit_tests_main.ml @@ -2,7 +2,7 @@ let suites = OUnit.( >::: ) __FILE__ [ Ounit_vec_test.suites; - Ounit_json_tests.suites; + Ounit_ext_json_tests.suites; Ounit_path_tests.suites; Ounit_array_tests.suites; Ounit_scc_tests.suites; From 775f0776093ca58bc54a11a8e7c003e3c03258cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Tue, 6 May 2025 14:52:41 +0100 Subject: [PATCH 2/4] Handle non-printable control characters when escaping JSON --- analysis/vendor/json/Json.ml | 5 ++++- tests/ounit_tests/dune | 2 +- tests/ounit_tests/ounit_json_tests.ml | 14 ++++++++++++++ tests/ounit_tests/ounit_tests_main.ml | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 tests/ounit_tests/ounit_json_tests.ml diff --git a/analysis/vendor/json/Json.ml b/analysis/vendor/json/Json.ml index 8bb6b8a363..407afb152b 100644 --- a/analysis/vendor/json/Json.ml +++ b/analysis/vendor/json/Json.ml @@ -141,7 +141,10 @@ let escape text = | '\b' -> Buffer.add_string buf "\\b" | '\r' -> Buffer.add_string buf "\\r" | '\t' -> Buffer.add_string buf "\\t" - | c -> Buffer.add_char buf c); + | c -> + let code = Char.code c in + if code < 0x20 then Printf.bprintf buf "\\u%04x" code + else Buffer.add_char buf c); loop (i + 1)) in loop 0; diff --git a/tests/ounit_tests/dune b/tests/ounit_tests/dune index 446de39f4d..56ac24bd1f 100644 --- a/tests/ounit_tests/dune +++ b/tests/ounit_tests/dune @@ -11,4 +11,4 @@ (<> %{profile} browser)) (flags (:standard -w +a-4-9-30-40-41-42-48-70)) - (libraries bsb bsb_helper core ounit2)) + (libraries bsb bsb_helper core ounit2 analysis)) diff --git a/tests/ounit_tests/ounit_json_tests.ml b/tests/ounit_tests/ounit_json_tests.ml new file mode 100644 index 0000000000..e096bce075 --- /dev/null +++ b/tests/ounit_tests/ounit_json_tests.ml @@ -0,0 +1,14 @@ +let ( >:: ), ( >::: ) = OUnit.(( >:: ), ( >::: )) + +let suites = + __FILE__ + >::: [ + ( "escape 'hello'" >:: fun _ -> + let escaped = Json.escape "hello" in + let expected = "hello" in + OUnit.assert_equal escaped expected ); + ( "escape \\x17" >:: fun _ -> + let escaped = Json.escape "\x17" in + let expected = "\\u0017" in + OUnit.assert_equal escaped expected ); + ] diff --git a/tests/ounit_tests/ounit_tests_main.ml b/tests/ounit_tests/ounit_tests_main.ml index cc0dee6302..37a1d7e597 100644 --- a/tests/ounit_tests/ounit_tests_main.ml +++ b/tests/ounit_tests/ounit_tests_main.ml @@ -2,6 +2,7 @@ let suites = OUnit.( >::: ) __FILE__ [ Ounit_vec_test.suites; + Ounit_json_tests.suites; Ounit_ext_json_tests.suites; Ounit_path_tests.suites; Ounit_array_tests.suites; From ccd0c6959add5695952c5509ce7977e038b7d9b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Tue, 6 May 2025 14:54:07 +0100 Subject: [PATCH 3/4] bsb watcher serialiser: handle non-printable control characters when escaping JSON --- compiler/ext/ext_json_noloc.ml | 63 ++++++++++++---------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/compiler/ext/ext_json_noloc.ml b/compiler/ext/ext_json_noloc.ml index 5f751f4fda..4977770cf0 100644 --- a/compiler/ext/ext_json_noloc.ml +++ b/compiler/ext/ext_json_noloc.ml @@ -33,48 +33,27 @@ type t = | Obj of t Map_string.t (** poor man's serialization *) -let naive_escaped (unmodified_input : string) : string = - let n = ref 0 in - let len = String.length unmodified_input in - for i = 0 to len - 1 do - n := - !n - + - match String.unsafe_get unmodified_input i with - | '\"' | '\\' | '\n' | '\t' | '\r' | '\b' -> 2 - | _ -> 1 - done; - if !n = len then unmodified_input - else - let result = Bytes.create !n in - n := 0; - for i = 0 to len - 1 do - let open Bytes in - (match String.unsafe_get unmodified_input i with - | ('\"' | '\\') as c -> - unsafe_set result !n '\\'; - incr n; - unsafe_set result !n c - | '\n' -> - unsafe_set result !n '\\'; - incr n; - unsafe_set result !n 'n' - | '\t' -> - unsafe_set result !n '\\'; - incr n; - unsafe_set result !n 't' - | '\r' -> - unsafe_set result !n '\\'; - incr n; - unsafe_set result !n 'r' - | '\b' -> - unsafe_set result !n '\\'; - incr n; - unsafe_set result !n 'b' - | c -> unsafe_set result !n c); - incr n - done; - Bytes.unsafe_to_string result +let naive_escaped (text : string) : string = + let ln = String.length text in + let buf = Buffer.create ln in + let rec loop i = + if i < ln then ( + (match text.[i] with + | '\012' -> Buffer.add_string buf "\\f" + | '\\' -> Buffer.add_string buf "\\\\" + | '"' -> Buffer.add_string buf "\\\"" + | '\n' -> Buffer.add_string buf "\\n" + | '\b' -> Buffer.add_string buf "\\b" + | '\r' -> Buffer.add_string buf "\\r" + | '\t' -> Buffer.add_string buf "\\t" + | c -> + let code = Char.code c in + if code < 0x20 then Printf.bprintf buf "\\u%04x" code + else Buffer.add_char buf c); + loop (i + 1)) + in + loop 0; + Buffer.contents buf let quot x = "\"" ^ naive_escaped x ^ "\"" From 841044e7f38bcc5bf2a7170757fe2628f9fc3cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9di-R=C3=A9mi=20Hashim?= Date: Tue, 6 May 2025 15:35:04 +0100 Subject: [PATCH 4/4] Added CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f667bae70..9ec2098e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Fix broken `bstracing` CLI location. https://github.com/rescript-lang/rescript/pull/7398 - Fix field flattening optimization to avoid creating unnecessary copies of allocating constants. https://github.com/rescript-lang/rescript-compiler/pull/7421 - Fix leading comments removed when braces inside JSX contains `let` assignment. https://github.com/rescript-lang/rescript/pull/7424 +- Fix JSON escaping in code editor analysis: JSON was not always escaped properly, which prevented code actions from being available in certain situations https://github.com/rescript-lang/rescript/pull/7435 #### :house: Internal