Thank you for filing! Check list:
A regex literal at the start of a statement, on the line after another statement, is parsed as a division that continues the previous line, so the file does not parse.
rescript format produces exactly this form from code that parses: it removes the parentheses around the literal.
Snippet
src/A.res:
let f = s => {
let t = s
(/a/)->RegExp.test(t)
}
npx rescript format src/A.res
cat src/A.res
npx rescript build
Actual
let f = s => {
let t = s
/a/->RegExp.test(t)
}
Cleaned 0/0
Error in check:
Syntax error!
/path/to/check/src/A.res:3:6-7
1 │ let f = s => {
2 │ let t = s
3 │ /a/->RegExp.test(t)
4 │ }
5 │
Did you forget to write an expression here?
Incremental build failed. Error: Could not parse Source Files
The formatted form fails the same way when written by hand, and so does a regex literal after an expression statement (Console.log(s)) or after a top-level let.
The formatter gives the same result for the other spellings that parse:
; at the end of the line before the literal (let t = s;) is removed;
- on 12.3.1,
%re("/a/")->RegExp.test(t) becomes /a/->RegExp.test(t).
These spellings parse and keep their form after formatting:
- the literal as the first statement of the block;
- the literal as a call argument:
RegExp.test(/a/, t);
- the literal bound to a name first:
let r = /a/.
Expected
The file parses: / at the start of a statement starts a regex literal.
If that is not possible, the formatter keeps the parentheses (or the ;) in front of such a literal.
Possible cause: in parse_binary_expr (compiler/syntax/src/res_core.ml), Minus | MinusDot | LessThan | Percent on a new line with no whitespace after it is not taken as a binary operator.
Forwardslash has no such case, so / on a new line always continues the expression on the line before.
Worked in ReScript 11
ReScript 11.1.4 has no regex literals, and its formatter keeps %re:
let f = s => {
let t = s
%re("/a/")->Js.Re.test_(t)
}
The same commands print the file unchanged and build it:
let f = s => {
let t = s
%re("/a/")->Js.Re.test_(t)
}
>>>> Start compiling
Dependency Finished
rescript: [1/3] src/A.ast
rescript: [2/3] src/A.d
rescript: [3/3] src/A.cmj
>>>> Finish compiling 19 mseconds
Environment
- ReScript 12.3.1, 13.0.0-alpha.6, and master
58c6c89 (from pkg.pr.new, reports 13.0.0-alpha.7): the same result on all three.
- Node.js 24.9.0
- Linux x86_64
Context
We hit it when rescript format 12.3.1 rewrote %re("/^[0-9\.,+-]*$/")->RegExp.test(str) ? Some(str) : None, which came right after a let line in a block.
Workaround
Pass the literal as an argument (RegExp.test(/a/, t)) or bind it with let first.
Thank you for filing! Check list:
A regex literal at the start of a statement, on the line after another statement, is parsed as a division that continues the previous line, so the file does not parse.
rescript formatproduces exactly this form from code that parses: it removes the parentheses around the literal.Snippet
src/A.res:Actual
The formatted form fails the same way when written by hand, and so does a regex literal after an expression statement (
Console.log(s)) or after a top-levellet.The formatter gives the same result for the other spellings that parse:
;at the end of the line before the literal (let t = s;) is removed;%re("/a/")->RegExp.test(t)becomes/a/->RegExp.test(t).These spellings parse and keep their form after formatting:
RegExp.test(/a/, t);let r = /a/.Expected
The file parses:
/at the start of a statement starts a regex literal.If that is not possible, the formatter keeps the parentheses (or the
;) in front of such a literal.Possible cause: in
parse_binary_expr(compiler/syntax/src/res_core.ml),Minus | MinusDot | LessThan | Percenton a new line with no whitespace after it is not taken as a binary operator.Forwardslashhas no such case, so/on a new line always continues the expression on the line before.Worked in ReScript 11
ReScript 11.1.4 has no regex literals, and its formatter keeps
%re:The same commands print the file unchanged and build it:
Environment
58c6c89(from pkg.pr.new, reports13.0.0-alpha.7): the same result on all three.Context
We hit it when
rescript format12.3.1 rewrote%re("/^[0-9\.,+-]*$/")->RegExp.test(str) ? Some(str) : None, which came right after aletline in a block.Workaround
Pass the literal as an argument (
RegExp.test(/a/, t)) or bind it withletfirst.