This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,38 @@
This tests how it is possible to disable formatting for a particular dialect in
a given subdirectory. This can be used to disable formatting of a particular
dune file.
$ cat > dune-project << EOF
> (lang dune 2.8)
> EOF
$ cat > dune << EOF
> ; this file should be formatted
> (rule (write-file a b))
> EOF
$ mkdir sub
$ cat > sub/dune << EOF
> ; this file should not
> (env
> (_
> (formatting (enabled_for ocaml))))
>
> (rule (write-file a b))
> EOF
$ dune build @fmt
File "dune", line 1, characters 0-0:
Error: Files _build/default/dune and _build/default/.formatted/dune differ.
[1]
Disable foramtting in the root directory using context settings
$ cat >dune-workspace <<EOF
> (lang dune 3.11)
> (env
> (_
> (formatting disabled)))
> EOF
$ dune build @fmt

View file

@ -0,0 +1,7 @@
(env
(_
(binaries ../../utils/ocamlformat.exe)))
(cram
(applies_to :whole_subtree)
(deps %{bin:ocamlformat}))

View file

@ -0,0 +1,6 @@
$ cat > dune-project << EOF
> (lang dune 3.5)
> (formatting)
> EOF
$ dune build @fmt

View file

@ -0,0 +1,16 @@
$ cat > dune-project << EOF
> (lang dune 3.5)
> EOF
$ cat > dune << EOF
> (env
> (_
> (formatting (enabled-with-typo a b c))))
> EOF
$ dune build @fmt
File "dune", line 3, characters 15-32:
3 | (formatting (enabled-with-typo a b c))))
^^^^^^^^^^^^^^^^^
Error: Unknown field "enabled-with-typo"
[1]

View file

@ -0,0 +1,3 @@
(library
(name lib)
)

View file

@ -0,0 +1,9 @@
(* -*- tuareg -*- *)
let
() =
Jbuild_plugin.V1.send {|
(alias
(name runtest)
(action (echo "ocaml syntax")))
|}

View file

@ -0,0 +1,2 @@
(lang dune 1.7)
(using fmt 1.1)

View file

@ -0,0 +1,2 @@
No format rules should be created for this directory, but no error should be
displayed (see #1479).

View file

@ -0,0 +1,3 @@
(library
(name lib_other_project)
)

View file

@ -0,0 +1,3 @@
(executables
(public_names ocamlformat refmt)
)

View file

@ -0,0 +1,8 @@
let process args =
Printf.printf "Sys.argv: %s\n" (String.concat " " (Array.to_list args));
Printf.printf "ocamlformat output\n"
let () =
match Sys.argv with
| [| _ ; _; _|] -> process Sys.argv
| _ -> assert false

View file

@ -0,0 +1,8 @@
let process args =
Printf.printf "Sys.argv: %s\n" (String.concat " " (Array.to_list args));
Printf.printf "refmt output\n"
let () =
match Sys.argv with
| [| _ ; _|] -> process Sys.argv
| _ -> assert false

View file

@ -0,0 +1,3 @@
let () =
print_endline
"e"

View file

@ -0,0 +1,3 @@
(lang dune 2.0)
(formatting disabled)

View file

@ -0,0 +1,3 @@
(library
(name lib)
)

View file

@ -0,0 +1,2 @@
(lang dune 1.4)
(using fmt 1.0 (enabled_for ocaml))

View file

@ -0,0 +1,253 @@
Note about versioning:
- in (lang dune 1.x), no formatting rules are set up by default.
- (lang dune 2.0) behaves as if (using fmt 1.2) is set.
Formatting can be checked using the @fmt target:
$ touch .ocamlformat
$ cat > enabled/ocaml_file.ml << EOF
> let y=()
> EOF
$ cat > enabled/reason_file.re << EOF
> let y = ();
> EOF
$ cat > enabled/dune << EOF
> (library
>
> (name
> lib_reason
>
> )
> )
> EOF
$ echo '(lang dune 2.0)' > dune-project
$ echo '(lang dune 2.0)' > lang2/default/dune-project
$ cat > lang2/partial/dune-project << EOF
> (lang dune 2.0)
>
> (formatting
> (enabled_for ocaml))
> EOF
$ dune build @fmt
File "enabled/dune", line 1, characters 0-0:
Error: Files _build/default/enabled/dune and
_build/default/enabled/.formatted/dune differ.
File "enabled/ocaml_file.ml", line 1, characters 0-0:
Error: Files _build/default/enabled/ocaml_file.ml and
_build/default/enabled/.formatted/ocaml_file.ml differ.
File "enabled/ocaml_file.mli", line 1, characters 0-0:
Error: Files _build/default/enabled/ocaml_file.mli and
_build/default/enabled/.formatted/ocaml_file.mli differ.
File "enabled/reason_file.re", line 1, characters 0-0:
Error: Files _build/default/enabled/reason_file.re and
_build/default/enabled/.formatted/reason_file.re differ.
File "enabled/reason_file.rei", line 1, characters 0-0:
Error: Files _build/default/enabled/reason_file.rei and
_build/default/enabled/.formatted/reason_file.rei differ.
File "enabled/subdir/dune", line 1, characters 0-0:
Error: Files _build/default/enabled/subdir/dune and
_build/default/enabled/subdir/.formatted/dune differ.
File "enabled/subdir/lib.ml", line 1, characters 0-0:
Error: Files _build/default/enabled/subdir/lib.ml and
_build/default/enabled/subdir/.formatted/lib.ml differ.
File "lang2/default/dune", line 1, characters 0-0:
Error: Files _build/default/lang2/default/dune and
_build/default/lang2/default/.formatted/dune differ.
File "lang2/default/e.ml", line 1, characters 0-0:
Error: Files _build/default/lang2/default/e.ml and
_build/default/lang2/default/.formatted/e.ml differ.
File "lang2/partial/a.ml", line 1, characters 0-0:
Error: Files _build/default/lang2/partial/a.ml and
_build/default/lang2/partial/.formatted/a.ml differ.
File "partial/a.ml", line 1, characters 0-0:
Error: Files _build/default/partial/a.ml and
_build/default/partial/.formatted/a.ml differ.
[1]
Configuration files are taken into account for this action:
$ touch enabled/.ocamlformat
$ dune build @fmt
File "enabled/dune", line 1, characters 0-0:
Error: Files _build/default/enabled/dune and
_build/default/enabled/.formatted/dune differ.
File "enabled/ocaml_file.ml", line 1, characters 0-0:
Error: Files _build/default/enabled/ocaml_file.ml and
_build/default/enabled/.formatted/ocaml_file.ml differ.
File "enabled/ocaml_file.mli", line 1, characters 0-0:
Error: Files _build/default/enabled/ocaml_file.mli and
_build/default/enabled/.formatted/ocaml_file.mli differ.
File "enabled/reason_file.re", line 1, characters 0-0:
Error: Files _build/default/enabled/reason_file.re and
_build/default/enabled/.formatted/reason_file.re differ.
File "enabled/reason_file.rei", line 1, characters 0-0:
Error: Files _build/default/enabled/reason_file.rei and
_build/default/enabled/.formatted/reason_file.rei differ.
File "enabled/subdir/dune", line 1, characters 0-0:
Error: Files _build/default/enabled/subdir/dune and
_build/default/enabled/subdir/.formatted/dune differ.
File "enabled/subdir/lib.ml", line 1, characters 0-0:
Error: Files _build/default/enabled/subdir/lib.ml and
_build/default/enabled/subdir/.formatted/lib.ml differ.
File "lang2/default/dune", line 1, characters 0-0:
Error: Files _build/default/lang2/default/dune and
_build/default/lang2/default/.formatted/dune differ.
File "lang2/default/e.ml", line 1, characters 0-0:
Error: Files _build/default/lang2/default/e.ml and
_build/default/lang2/default/.formatted/e.ml differ.
File "lang2/partial/a.ml", line 1, characters 0-0:
Error: Files _build/default/lang2/partial/a.ml and
_build/default/lang2/partial/.formatted/a.ml differ.
File "partial/a.ml", line 1, characters 0-0:
Error: Files _build/default/partial/a.ml and
_build/default/partial/.formatted/a.ml differ.
[1]
And fixable files can be promoted:
$ dune promote enabled/ocaml_file.ml enabled/reason_file.re enabled/dune
Promoting _build/default/enabled/.formatted/dune to enabled/dune.
Promoting _build/default/enabled/.formatted/ocaml_file.ml to
enabled/ocaml_file.ml.
Promoting _build/default/enabled/.formatted/reason_file.re to
enabled/reason_file.re.
$ cat enabled/ocaml_file.ml
Sys.argv: ../install/default/bin/ocamlformat --impl enabled/ocaml_file.ml
ocamlformat output
$ cat enabled/reason_file.re
Sys.argv: ../install/default/bin/refmt enabled/reason_file.re
refmt output
$ cat enabled/dune
(library
(name lib_reason))
The fmt command automatically promotes the formatted files:
$ touch fmt-cmd/.ocamlformat
$ cat > fmt-cmd/ocaml_file.ml << EOF
> let y=()
> EOF
$ cat > fmt-cmd/reason_file.re << EOF
> let y = ();
> EOF
$ (cd fmt-cmd && dune fmt)
$ cat fmt-cmd/ocaml_file.ml
let y=()
$ cat fmt-cmd/reason_file.re
let y = ();
All .ocamlformat files are considered dependencies:
$ echo 'margin = 70' > .ocamlformat
$ dune build @fmt
File "enabled/ocaml_file.mli", line 1, characters 0-0:
Error: Files _build/default/enabled/ocaml_file.mli and
_build/default/enabled/.formatted/ocaml_file.mli differ.
File "enabled/reason_file.rei", line 1, characters 0-0:
Error: Files _build/default/enabled/reason_file.rei and
_build/default/enabled/.formatted/reason_file.rei differ.
File "enabled/subdir/dune", line 1, characters 0-0:
Error: Files _build/default/enabled/subdir/dune and
_build/default/enabled/subdir/.formatted/dune differ.
File "enabled/subdir/lib.ml", line 1, characters 0-0:
Error: Files _build/default/enabled/subdir/lib.ml and
_build/default/enabled/subdir/.formatted/lib.ml differ.
File "lang2/default/dune", line 1, characters 0-0:
Error: Files _build/default/lang2/default/dune and
_build/default/lang2/default/.formatted/dune differ.
File "lang2/default/e.ml", line 1, characters 0-0:
Error: Files _build/default/lang2/default/e.ml and
_build/default/lang2/default/.formatted/e.ml differ.
File "lang2/partial/a.ml", line 1, characters 0-0:
Error: Files _build/default/lang2/partial/a.ml and
_build/default/lang2/partial/.formatted/a.ml differ.
File "partial/a.ml", line 1, characters 0-0:
Error: Files _build/default/partial/a.ml and
_build/default/partial/.formatted/a.ml differ.
[1]
Mixing (lang dune 2.0) and (using fmt 1.0) is an error.
But a helpful message is displayed.
$ cp lang2/partial/dune-project lang2/partial/dune-project.bak
$ echo '(using fmt 1.0)' >> lang2/partial/dune-project
$ dune build @lang2/partial/fmt
File "lang2/partial/dune-project", line 5, characters 0-15:
5 | (using fmt 1.0)
^^^^^^^^^^^^^^^
Error: Starting with (lang dune 2.0), formatting is enabled by default.
To port it to the new syntax, you can replace this part by:
(formatting (enabled_for ocaml reason))
[1]
$ mv lang2/partial/dune-project.bak lang2/partial/dune-project
Sometimes, the suggestion is to just remove the configuration.
$ echo '(using fmt 1.2)' >> lang2/partial/dune-project
$ dune build @lang2/partial/fmt
File "lang2/partial/dune-project", line 5, characters 0-15:
5 | (using fmt 1.2)
^^^^^^^^^^^^^^^
Error: Starting with (lang dune 2.0), formatting is enabled by default.
To port it to the new syntax, you can delete this part.
[1]
Formatting can also be set in the (env ...) stanza
$ mkdir -p using-env
$ cat >using-env/dune-project <<EOF
> (lang dune 2.7)
> EOF
$ cat >using-env/dune <<EOF
> (env (_ (formatting disabled)))
> EOF
$ mkdir -p using-env/subdir
$ cat >using-env/subdir/dune <<EOF
> (executable (name foo))
> EOF
$ cat >using-env/subdir/foo.ml <<EOF
> let x = 12
> EOF
$ (cd using-env && dune build @fmt)
File "dune", line 1, characters 8-29:
1 | (env (_ (formatting disabled)))
^^^^^^^^^^^^^^^^^^^^^
Error: 'formatting' is only available since version 2.8 of the dune language.
Please update your dune-project file to have (lang dune 2.8).
[1]
$ cat >using-env/dune-project <<EOF
> (lang dune 2.8)
> EOF
$ (cd using-env && dune build @fmt)
$ cat >using-env/dune <<EOF
> (env (_ (formatting (enabled_for ocaml))))
> EOF
$ touch using-env/.ocamlformat
$ (cd using-env && dune build @fmt)
fake ocamlformat is running: "--impl" "subdir/foo.ml"
File "subdir/foo.ml", line 1, characters 0-0:
Error: Files _build/default/subdir/foo.ml and
_build/default/subdir/.formatted/foo.ml differ.
[1]
We check that the formatting stanza in (env ...) takes precedence over that in
dune-project:
$ cat >>using-env/dune-project <<EOF
> (formatting disabled)
> EOF
$ (cd using-env && dune build @fmt)
File "subdir/foo.ml", line 1, characters 0-0:
Error: Files _build/default/subdir/foo.ml and
_build/default/subdir/.formatted/foo.ml differ.
[1]
Next we check that the new logic does not interfere with default per-project
settings as dictated by the dune language version.
$ cat >using-env/subdir/dune-project <<EOF
> (lang dune 1.7)
> ;; formatting disabled by default
> EOF
$ (cd using-env && dune build @fmt)

View file

@ -0,0 +1,34 @@
$ cat > dune-project << EOF
> (lang dune 3.0)
> EOF
Make a dune file that should be formatted.
$ cat > dune << EOF
> (rule (write-file a b))
> EOF
Run `dune fmt --preview` twice to test it is idempotent. In a terminal these
commands would also print the diff of what would be changed.
$ dune fmt --preview
File "dune", line 1, characters 0-0:
Error: Files _build/default/dune and _build/default/.formatted/dune differ.
[1]
$ dune fmt --preview
File "dune", line 1, characters 0-0:
Error: Files _build/default/dune and _build/default/.formatted/dune differ.
[1]
Show the formatted file from _build.
$ cat _build/default/.formatted/dune
(rule
(write-file a b))
Actually format the file
$ dune fmt
File "dune", line 1, characters 0-0:
Error: Files _build/default/dune and _build/default/.formatted/dune differ.
Promoting _build/default/.formatted/dune to dune.
[1]
Now the output of `dune fmt --preview is empty`.
$ dune fmt --preview

View file

@ -0,0 +1,24 @@
Dune files names dune-file should be formatted
$ cat >dune-project <<EOF
> (lang dune 3.5)
> (accept_alternative_dune_file_name)
> EOF
$ cat >dune-file <<EOF
> (rule
> (with-stdout-to foo (echo bar)))
> EOF
$ dune fmt
File "dune-file", line 1, characters 0-0:
Error: Files _build/default/dune-file and _build/default/.formatted/dune-file
differ.
Promoting _build/default/.formatted/dune-file to dune-file.
[1]
$ cat dune-file
(rule
(with-stdout-to
foo
(echo bar)))

View file

@ -0,0 +1,7 @@
(* -*- tuareg -*- *)
let () = Jbuild_plugin.V1.send {|
(alias
(name runtest)
(action (echo "ocaml syntax")))
|}

View file

@ -0,0 +1,261 @@
The empty list and atoms are printed as is:
$ echo '()' | dune format-dune-file
()
$ echo 'a' | dune format-dune-file
a
Lists containing only atoms, quoted strings, templates, and singleton lists are
printed inline:
$ echo '(atom "string" %{template} (singleton))' | dune format-dune-file
(atom "string" %{template} (singleton))
Other lists are displayed one element per line:
$ echo '(a (b c d) e)' | dune format-dune-file
(a
(b c d)
e)
When there are several s-expressions, they are printed with an empty line
between them:
$ echo '(a b) (c d)' | dune format-dune-file
(a b)
(c d)
It is possible to pass a file name:
$ dune format-dune-file dune.dune
(a b)
Parse errors are displayed:
$ echo '(' | dune format-dune-file
File "", line 2, characters 0-0:
Error: unclosed parenthesis at end of input
[1]
When a list is indented, there is no extra space at the end.
$ echo ' (a (b (c d)))' | dune format-dune-file
(a
(b
(c d)))
When there is a long list of atoms, quoted strings, templates and singletons,
it gets wrapped.
$ echo '(library (name dune) (libraries unix stdune fiber xdg dune_re threads opam_file_format dune_lang ocaml_config which_program) (synopsis "Internal Dune library, do not use!") (preprocess (action (run %{project_root}/src/let-syntax/pp.exe %{input-file}))))' | dune format-dune-file --dune-version 3.16
(library
(name dune)
(libraries
unix
stdune
fiber
xdg
dune_re
threads
opam_file_format
dune_lang
ocaml_config
which_program)
(synopsis "Internal Dune library, do not use!")
(preprocess
(action
(run %{project_root}/src/let-syntax/pp.exe %{input-file}))))
The same file, but in the current version:
$ echo '(library (name dune) (libraries unix stdune fiber xdg dune_re threads opam_file_format dune_lang ocaml_config which_program) (synopsis "Internal Dune library, do not use!") (preprocess (action (run %{project_root}/src/let-syntax/pp.exe %{input-file}))))' | dune format-dune-file
(library
(name dune)
(libraries
unix
stdune
fiber
xdg
dune_re
threads
opam_file_format
dune_lang
ocaml_config
which_program)
(synopsis "Internal Dune library, do not use!")
(preprocess
(action
(run %{project_root}/src/let-syntax/pp.exe %{input-file}))))
In multi-line strings, newlines are escaped, but their syntax is not preserved.
$ dune format-dune-file <<EOF
> (echo "\> multi
> "\> line
> "\> string
> "\| string
> )
>
> (echo "\
> multi
> line
> string
> ")
> EOF
(echo "multi\nline\nstring\nstring\n")
(echo "multi\nline\nstring\n")
Comments are preserved.
$ dune format-dune-file <<EOF
> ; comment
> (; first comment
> a b; comment for b
> ccc; multi
> ; line
> ; comment for ccc
> d
> e
> ; unattached comment
> f
> ; unattached
> ; multi-line
> ; comment
> g
> )
> EOF
; comment
(; first comment
a
b ; comment for b
ccc ; multi
; line
; comment for ccc
d
e
; unattached comment
f
; unattached
; multi-line
; comment
g)
When a comment is at the end of a list, the ")" is on a own line.
$ dune format-dune-file <<EOF
> (a ; final attached comment
> )
> (a ; final multiline
> ; comment
> )
> (a
> ; final unattached
> )
> (a
> ; final unattached
> ; multiline
> )
> EOF
(a ; final attached comment
)
(a ; final multiline
; comment
)
(a
; final unattached
)
(a
; final unattached
; multiline
)
Files in OCaml syntax are copied verbatim (but error when passed in stdin).
$ dune format-dune-file < ocaml-syntax.dune
File "", line 1, characters 0-20:
Error: OCaml syntax is not supported.
[1]
$ dune format-dune-file ocaml-syntax.dune
(* -*- tuareg -*- *)
let () = Jbuild_plugin.V1.send {|
(alias
(name runtest)
(action (echo "ocaml syntax")))
|}
Non 0 error code:
$ echo "(" | dune format-dune-file ; echo $?
File "", line 2, characters 0-0:
Error: unclosed parenthesis at end of input
1
Using the built-in action.
$ cat >dune-project <<EOF
> (lang dune 3.18)
> EOF
$ cat >dune <<EOF
> (rule (with-stdout-to file (echo "( a c)")))
> (rule (format-dune-file file file.formatted))
> EOF
$ dune build file.formatted
$ cat _build/default/file.formatted
(a c)
Version check.
$ cat >dune-project <<EOF
> (lang dune 3.17)
> EOF
$ dune build file.out
File "dune", line 2, characters 0-45:
2 | (rule (format-dune-file file file.formatted))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'format-dune-file' in short-form 'rule' is only available since
version 3.18 of the dune language. Please update your dune-project file to
have (lang dune 3.18).
[1]
$ cat >dune <<EOF
> (rule (with-stdout-to file (echo "( a c)")))
> (rule (action (format-dune-file file file.formatted)))
> EOF
$ dune build file.out
File "dune", line 2, characters 14-52:
2 | (rule (action (format-dune-file file file.formatted)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'format-dune-file' is only available since version 3.18 of the dune
language. Please update your dune-project file to have (lang dune 3.18).
[1]
Behaviour when the dune file is not syntactically valid.
$ cat >dune-project <<EOF
> (lang dune 3.18)
> EOF
$ cat >dune <<EOF
> (rule (with-stdout-to file (echo "xxx yyy (")))
> (rule (format-dune-file file file.formatted))
> EOF
$ dune build file.formatted
File "_build/default/file", line 1, characters 9-9:
1 | xxx yyy (
Error: unclosed parenthesis at end of input
[1]

View file

@ -0,0 +1,20 @@
$ touch .ocamlformat
$ cat > dune-project << EOF
> (lang dune 3.7)
> EOF
$ mkdir bin
$ cat > bin/ocaml_file.ml << EOF
> let y=()
> EOF
$ cat > dune << EOF
> (include_subdirs unqualified)
> (library (name lib_reason))
> EOF
$ dune build ./bin/.formatted/ocaml_file.ml
fake ocamlformat is running: "--impl" "bin/ocaml_file.ml"
.formatted dir is loaded
$ ls _build/default/bin/.formatted
ocaml_file.ml

View file

@ -0,0 +1,80 @@
See issue 7454
$ cat >parser_raw.mly <<EOF
> %token EOL
> %start <unit> prog
> %%
> prog:
> | EOL { () }
> | EOL; prog { () }
> EOF
$ cat >dune <<EOF
> (library
> (name foo)
> (modules parser_raw other_gen))
>
> (menhir
> (modules parser_raw)
> (mode promote))
>
> (rule
> (targets other_gen.ml)
> (mode promote)
> (action (with-stdout-to %{targets}
> (echo "print_int 42"))))
> EOF
$ cat >dune-project <<EOF
> (lang dune 2.9)
> (using menhir 2.0)
> (formatting (enabled_for ocaml))
> EOF
$ touch .ocamlformat
In the above project, two modules have their implementation generated:
- [Other_gen]'s implementation is generated by a rule stanza
- [Parser_raw]'s implementation is generated by menhir stanza
Formatting the codebase should not trigger the generation of missing modules
$ dune build @fmt
As expected, Dune did not try to generate the missing implem for [Parser_raw]
$ dune_cmd exists _build/default/parser_raw.ml
false
As expected, Dune did not try to generate the missing implem for [Other_gen]
$ dune_cmd exists _build/default/other_gen.ml
false
$ dune clean
Now we add [mli] files for the two modules whose implementation is generated:
$ touch other_gen.mli
$ touch parser_raw.mli
We format again.
$ dune build @fmt
fake ocamlformat is running: "--intf" "other_gen.mli"
Warning: one state end-of-stream conflict was arbitrarily resolved.
File "parser_raw.mly", line 5, characters 4-7:
Warning: production prog -> EOL is never reduced.
Warning: in total, 1 production is never reduced.
fake ocamlformat is running: "--intf" "parser_raw.mli"
File "other_gen.mli", line 1, characters 0-0:
Error: Files _build/default/other_gen.mli and
_build/default/.formatted/other_gen.mli differ.
File "parser_raw.mli", line 1, characters 0-0:
Error: Files _build/default/parser_raw.mli and
_build/default/.formatted/parser_raw.mli differ.
[1]
FIXME: unexpectedly, Dune generated the missing parser
$ dune_cmd exists _build/default/parser_raw.ml
true
As expected, Dune did not try to generate the missing implem for [Other_gen]
$ dune_cmd exists _build/default/other_gen.ml
false

View file

@ -0,0 +1,35 @@
Utf8 characters are handled for now, this is also related to the issue #9728
$ dune format-dune-file <<EOF
> ("Éff ĎúÑȨ")
> EOF
("Éff ĎúÑȨ")
$ dune format-dune-file <<EOF
> (run foo %{bin:é})
> EOF
File "", line 1, characters 15-16:
Error: The character '\195' is not allowed inside %{...} forms
[1]
$ dune format-dune-file <<EOF
> (echo "hÉllo")
> EOF
(echo "hÉllo")
$ dune format-dune-file <<EOF
> (echo "É")
> EOF
(echo "É")
$ dune format-dune-file <<EOF
> (Écho "hello")
> EOF
File "", line 1, characters 1-1:
Error: Invalid . file
[1]
$ bash -c "printf '(echo \"%b\")' '\xc0'"| dune format-dune-file
(echo "\192")
$ bash -c "printf '(echo \"%b\")' '\xf0'"| dune format-dune-file
(echo "\240")