Closed copy closed 2 years ago
Trying to narrow this down, since it makes it quite difficult to work with ppxs in my monorepo:
I added the following here to check if merlin is being invoked with the correct ppx and that the ppx is built:
let ppx = config.ocaml.ppx in
List.iter ~f:(fun x ->
let f = List.hd (String.split_on_char ~sep:' ' x.Merlin_utils.Std.workval) in
Printf.eprintf "XXX %s %b\n%!" x.workval (Sys.file_exists f);
) ppx;
Printf.eprintf "XXX %s\n%!" (Merlin_utils.Std.Json.to_string (Mconfig.dump config));
I also added logging to make sure that the diagnostic is actually coming from merlin.
[ERROR][2022-05-31 17:02:57] .../vim/lsp/rpc.lua:420 "rpc" "/home/fabian/ocaml/not-mine/ocaml-lsp/_build/default/ocaml-lsp-server/bin/main.exe" "stderr" 'XXX /tmp/1/_build/default/.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe --as-ppx true\nXXX {"ocaml":{"include_dirs":[],"no_std_include":false,"unsafe":false,"classic":false,"principal":false,"real_paths":false,"recursive_types":false,"strict_sequence":true,"applicative_functors":true,"unsafe_string":false,"nopervasives":false,"strict_formats":true,"open_modules":[],"ppx":[{"workdir":"/tmp/1","workval":"/tmp/1/_build/default/.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe --as-ppx"}],"pp":null,"warnings":{"actives":[1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,30,31,32,33,34,35,36,37,38,39,43,46,47,49,50,51,52,53,54,55,56,57,58,59,61,62,63,64,65,71,72],"warn_error":[1,2,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,30,31,32,33,34,35,36,37,38,39,43,46,47,49,50,51,52,53,54,55,56,57,61,62],"alerts":{"alerts":[],"complement":true},"alerts_error":{"alerts":["deprecated"],"complement":false}}},"merlin":{"build_path":["/tmp/1/_build/default/.test.eobjs/byte"],"source_path":["/tmp/1"],"cmi_path":[],"cmt_path":[],"flags_applied":[{"workdir":"/tmp/1","workval":["-ppx","/tmp/1/_build/default/.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe --as-ppx"]},{"workdir":"/tmp/1","workval":["-w","@1..3@5..28@30..39@43@46..47@49..57@61..62-40","-strict-sequence","-strict-formats","-short-paths","-keep-locs"]}],"extensions":[],"suffixes":[{"impl":".ml","intf":".mli"},{"impl":".re","intf":".rei"}],"stdlib":"/home/fabian/.opam/4.14.0/lib/ocaml","reader":[],"protocol":"json","log_file":null,"log_sections":[],"flags_to_apply":[],"failures":[],"assoc_suffixes":[{"extension":".re","reader":"reason"},{"extension":".rei","reader":"reason"}]},"query":{"filename":"test.ml","directory":"/tmp/1","printer_width":0,"verbosity":0}}\n'
[ERROR][2022-05-31 17:02:57] .../vim/lsp/rpc.lua:420 "rpc" "/home/fabian/ocaml/not-mine/ocaml-lsp/_build/default/ocaml-lsp-server/bin/main.exe" "stderr" "XXX MERLIN DIAG: Uninterpreted extension 'test'.\n"
[DEBUG][2022-05-31 17:02:57] .../vim/lsp/rpc.lua:454 "rpc.receive" { jsonrpc = "2.0", method = "textDocument/publishDiagnostics", params = { diagnostics = { { message = "Uninterpreted extension 'test'.", range = { end = { character = 8, line = 0 }, start = { character = 4, line = 0 } }, severity = 1, source = "ocamllsp" } }, uri = "file:///tmp/1/test.ml" }}
Resulting config:
{
"ocaml": {
"include_dirs": [],
"no_std_include": false,
"unsafe": false,
"classic": false,
"principal": false,
"real_paths": false,
"recursive_types": false,
"strict_sequence": true,
"applicative_functors": true,
"unsafe_string": false,
"nopervasives": false,
"strict_formats": true,
"open_modules": [],
"ppx": [{
"workdir": "/tmp/1",
"workval": "/tmp/1/_build/default/.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe --as-ppx"
}],
"pp": null,
"warnings": {
"actives": [1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 43, 46, 47, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 61, 62, 63, 64, 65, 71, 72],
"warn_error": [1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 43, 46, 47, 49, 50, 51, 52, 53, 54, 55, 56, 57, 61, 62],
"alerts": {
"alerts": [],
"complement": true
},
"alerts_error": {
"alerts": ["deprecated"],
"complement": false
}
}
},
"merlin": {
"build_path": ["/tmp/1/_build/default/.test.eobjs/byte"],
"source_path": ["/tmp/1"],
"cmi_path": [],
"cmt_path": [],
"flags_applied": [{
"workdir": "/tmp/1",
"workval": ["-ppx", "/tmp/1/_build/default/.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe --as-ppx"]
}, {
"workdir": "/tmp/1",
"workval": ["-w", "@1..3@5..28@30..39@43@46..47@49..57@61..62-40", "-strict-sequence", "-strict-formats", "-short-paths", "-keep-locs"]
}],
"extensions": [],
"suffixes": [{
"impl": ".ml",
"intf": ".mli"
}, {
"impl": ".re",
"intf": ".rei"
}],
"stdlib": "/home/fabian/.opam/4.14.0/lib/ocaml",
"reader": [],
"protocol": "json",
"log_file": null,
"log_sections": [],
"flags_to_apply": [],
"failures": [],
"assoc_suffixes": [{
"extension": ".re",
"reader": "reason"
}, {
"extension": ".rei",
"reader": "reason"
}]
},
"query": {
"filename": "test.ml",
"directory": "/tmp/1",
"printer_width": 0,
"verbosity": 0
}
}
Unfortunately, everything looks correct. The correct ppx is being passed to merlin, as per dune rules:
((deps
((File
(In_build_dir
_build/default/.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe))
(File (In_build_dir _build/default/test.ml))))
(targets ((In_build_dir _build/default/test.pp.ml)))
(context default)
(action
(chdir
_build/default
(progn
(chdir
.
(run
.ppx/98cd9c27bc47def1a842c7a721af4e6b/ppx.exe
-o
test.pp.ml
--impl
test.ml
-corrected-suffix
.ppx-corrected
-diff-cmd
-
-dump-ast))
(diff? test.ml test.ml.ppx-corrected)))))
The error is the same as running ocaml without a ppx. What could cause merlin to "skip" the ppx, or how could I narrow it down further?
One thing that would help narrow this down: is it an issue with regular merlin?
No, with regular merlin the issue doesn't happen.
Narrowing it down further, I logged the potential exception that is caught in merlin's ppx code (here):
let caught = ref [] in
Msupport.catch_errors Mconfig.(config.ocaml.warnings) caught @@ fun () ->
Printf.eprintf "XXX CONFIG IN MPIPELINE: %s\n%!" (Json.to_string (Mconfig.dump config));
let parsetree = Mppx.rewrite config parsetree in
List.iter (fun e -> Printf.eprintf "EXCEPTION: %s\n%!" (Printexc.to_string e)) !caught;
{ Ppx. config; parsetree; errors = !caught }
And it prints:
"EXCEPTION: Sys_error(\"cd '/tmp/1' && /tmp/1/_build/default/.ppx/d7394c27c5e0f7ad7ab1110d6b092c05/ppx.exe --as-ppx '/tmp/camlppxb9d449' '/tmp/camlppxb579b9' 1>&2: No child process\")
Try this fix:
diff --git a/src/ocaml/driver/pparse.ml b/src/ocaml/driver/pparse.ml
index ab397e93..1679a05a 100644
--- a/src/ocaml/driver/pparse.ml
+++ b/src/ocaml/driver/pparse.ml
@@ -46,7 +46,7 @@ let merlin_system_command =
windows_merlin_system_command
else
fun cmd ~cwd ->
- Sys.command (Printf.sprintf "cd %s && %s" (Filename.quote cwd) cmd)
+ Sys.command (Printf.sprintf "cd %s && %s" (Filename.quote_command cwd) cmd)
let ppx_commandline cmd fn_in fn_out =
Printf.sprintf "%s %s %s%s"
In the vendored submodule of merlin in ocamllsp
Actually, never mind. I think I understand the issue. The scheduler is calling waitpid and that's probably reaping it for the ppx.
Try #735 and let me know if it fixes the issue.
I can confirm that #735 fixes the issue for me.
Consider this simplified executable and its ppx rewriter:
After running
dune build test.exe
once, and making trivial changes to either ppx.ml or test.ml, ocaml-lsp reportsUninterpreted extension
errors in either of the files. The errors are spurious and sometimes go away after making another change. The symptoms are a bit like if ocaml-lsp was running the incorrect preprocessor (e.g. ppxlib.metaquot on test.ml or ppx.exe on ppx.ml).Here are two logs from nvim (the first one with a spurious error in ppx.ml and the second with an error in test.ml):