ocaml / ocaml-lsp

OCaml Language Server Protocol implementation
Other
755 stars 117 forks source link

`Uninterpreted extension` with ppx rewriter in the same project #696

Closed copy closed 2 years ago

copy commented 2 years ago

Consider this simplified executable and its ppx rewriter:

(* test.ml *)
let%test _ = assert true
(* ppx.ml *)
open Ppxlib
open Ast_pattern

let pat = pstr ((pstr_value nonrecursive ((value_binding ~pat:ppat_any ~expr:__) ^:: nil)) ^:: nil)

let test =
  let expand ~loc ~path:_ e =
    let loc = { loc with loc_ghost = true } in
    match Sys.getenv_opt "RELEASE" with
    | Some _ -> Attribute.explicitly_drop#expression e; [%str ]
    | None -> [%str let () = if Sys.getenv_opt "DO_TEST" <> None then [%e e]]
  in
  Extension.declare_inline "ppx.test"
    Extension.Context.structure_item
    pat
    expand

let () = Driver.register_transformation "ppx" ~extensions:[test]
(executable
 (name test)
 (modules test)
 (preprocess
  (pps ppx)))

(library
 (name ppx)
 (modules ppx)
 (kind (ppx_rewriter))
 (preprocess
  (pps ppxlib.metaquot))
 (libraries ppxlib))

After running dune build test.exe once, and making trivial changes to either ppx.ml or test.ml, ocaml-lsp reports Uninterpreted 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):

[START][2022-05-08 18:42:46] LSP logging initiated
[INFO][2022-05-08 18:42:47] .../vim/lsp/rpc.lua:261 "Starting RPC client"   {  args = {},  cmd = "ocamllsp",  extra = {    cwd = "/tmp/1",    env = {      OCAMLLSP_DEBUG = "1"    }  }}
[DEBUG][2022-05-08 18:42:47] .../vim/lsp/rpc.lua:347    "rpc.send"  {  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      callHierarchy = {        dynamicRegistration = false      },      textDocument = {        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = false,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = false,            deprecatedSupport = false,            documentationFormat = { "markdown", "plaintext" },            preselectSupport = false,            snippetSupport = false          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          contextSupport = false,          dynamicRegistration = false        },        declaration = {          linkSupport = true        },        definition = {          linkSupport = true        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = false        },        implementation = {          linkSupport = true        },        publishDiagnostics = {          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = false,          prepareSupport = true        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = false,          willSaveWaitUntil = false        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = false        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        symbol = {          dynamicRegistration = false,          hierarchicalWorkspaceSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.7.0"    },    initializationOptions = vim.empty_dict(),    processId = 63252,    rootPath = "/tmp/1",    rootUri = "file:///tmp/1",    trace = "off",    workspaceFolders = { {        name = "/tmp/1",        uri = "file:///tmp/1"      } }  }}
[DEBUG][2022-05-08 18:42:47] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      codeActionProvider = {        codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }      },      codeLensProvider = {        resolveProvider = false      },      completionProvider = {        resolveProvider = true,        triggerCharacters = { ".", "#" }      },      declarationProvider = true,      definitionProvider = true,      documentFormattingProvider = true,      documentHighlightProvider = true,      documentSymbolProvider = true,      executeCommandProvider = {        commands = { "dune/promote" }      },      experimental = {        ocamllsp = {          diagnostic_promotions = true,          handleInferIntf = true,          handleSwitchImplIntf = true,          handleTypedHoles = true,          handleWrappingAstNode = true,          interfaceSpecificLangId = true        }      },      foldingRangeProvider = true,      hoverProvider = true,      referencesProvider = true,      renameProvider = {        prepareProvider = true      },      selectionRangeProvider = true,      signatureHelpProvider = {        triggerCharacters = { " ", "~", "?", ":", "(" }      },      textDocumentSync = {        change = 2,        openClose = true,        save = true,        willSave = false,        willSaveWaitUntil = false      },      typeDefinitionProvider = true,      workspace = {        workspaceFolders = {          changeNotifications = true,          supported = true        }      },      workspaceSymbolProvider = true    },    serverInfo = {      name = "ocamllsp",      version = "1.11.4"    }  }}
[DEBUG][2022-05-08 18:42:47] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[DEBUG][2022-05-08 18:42:47] .../lua/vim/lsp.lua:982    "LSP[ocamllsp]" "server_capabilities"   {  codeActionProvider = {    codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }  },  codeLensProvider = {    resolveProvider = false  },  completionProvider = {    resolveProvider = true,    triggerCharacters = { ".", "#" }  },  declarationProvider = true,  definitionProvider = true,  documentFormattingProvider = true,  documentHighlightProvider = true,  documentSymbolProvider = true,  executeCommandProvider = {    commands = { "dune/promote" }  },  experimental = {    ocamllsp = {      diagnostic_promotions = true,      handleInferIntf = true,      handleSwitchImplIntf = true,      handleTypedHoles = true,      handleWrappingAstNode = true,      interfaceSpecificLangId = true    }  },  foldingRangeProvider = true,  hoverProvider = true,  referencesProvider = true,  renameProvider = {    prepareProvider = true  },  selectionRangeProvider = true,  signatureHelpProvider = {    triggerCharacters = { " ", "~", "?", ":", "(" }  },  textDocumentSync = {    change = 2,    openClose = true,    save = true,    willSave = false,    willSaveWaitUntil = false  },  typeDefinitionProvider = true,  workspace = {    workspaceFolders = {      changeNotifications = true,      supported = true    }  },  workspaceSymbolProvider = true}
[INFO][2022-05-08 18:42:47] .../lua/vim/lsp.lua:983 "LSP[ocamllsp]" "initialized"   {  resolved_capabilities = {    call_hierarchy = false,    code_action = {      codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }    },    code_lens = true,    code_lens_resolve = false,    completion = true,    declaration = true,    document_formatting = true,    document_highlight = true,    document_range_formatting = false,    document_symbol = true,    execute_command = true,    find_references = true,    goto_definition = true,    hover = true,    implementation = false,    rename = true,    signature_help = true,    signature_help_trigger_characters = { " ", "~", "?", ":", "(" },    text_document_did_change = 2,    text_document_open_close = true,    text_document_save = true,    text_document_save_include_text = false,    text_document_will_save = false,    text_document_will_save_wait_until = false,    type_definition = true,    workspace_folder_properties = {      changeNotifications = true,      supported = true    },    workspace_symbol = true  }}
[DEBUG][2022-05-08 18:42:47] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "ocaml",      text = "let%test _ = assert true\n",      uri = "file:///tmp/1/test.ml",      version = 0    }  }}
[DEBUG][2022-05-08 18:42:47] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = {},    uri = "file:///tmp/1/test.ml"  }}
[DEBUG][2022-05-08 18:42:51] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "ocaml",      text = 'open Ppxlib\nopen Ast_pattern\n\nlet pat = pstr ((pstr_value nonrecursive ((value_binding ~pat:ppat_any ~expr:__) ^:: nil)) ^:: nil)\n\nlet test =\n  let expand ~loc ~path:_ e =\n    let loc = { loc with loc_ghost = true } in\n    match Sys.getenv_opt "RELEASE" with\n    | Some _ -> Attribute.explicitly_drop#expression e; [%str ]\n    | None -> [%str let () = if Sys.getenv_opt "DO_TEST" <> None then [%e e]]\n  in\n  Extension.declare_inline "ppx.test"\n    Extension.Context.structure_item\n    pat\n    expand\n\nlet () = Driver.register_transformation "ppx" ~extensions:[test]\n',      uri = "file:///tmp/1/ppx.ml",      version = 0    }  }}
[DEBUG][2022-05-08 18:42:52] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = { {        message = "Error (warning 26): unused variable loc.",        range = {          end = {            character = 11,            line = 7          },          start = {            character = 8,            line = 7          }        },        severity = 2,        source = "ocamllsp"      }, {        message = "Uninterpreted extension 'str'.",        range = {          end = {            character = 61,            line = 9          },          start = {            character = 58,            line = 9          }        },        severity = 1,        source = "ocamllsp"      }, {        message = "Uninterpreted extension 'str'.",        range = {          end = {            character = 19,            line = 10          },          start = {            character = 16,            line = 10          }        },        severity = 1,        source = "ocamllsp"      } },    uri = "file:///tmp/1/ppx.ml"  }}
[START][2022-05-08 18:43:57] LSP logging initiated
[INFO][2022-05-08 18:43:57] .../vim/lsp/rpc.lua:261 "Starting RPC client"   {  args = {},  cmd = "ocamllsp",  extra = {    cwd = "/tmp/1",    env = {      OCAMLLSP_DEBUG = "1"    }  }}
[DEBUG][2022-05-08 18:43:57] .../vim/lsp/rpc.lua:347    "rpc.send"  {  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      callHierarchy = {        dynamicRegistration = false      },      textDocument = {        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = false,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = false,            deprecatedSupport = false,            documentationFormat = { "markdown", "plaintext" },            preselectSupport = false,            snippetSupport = false          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          contextSupport = false,          dynamicRegistration = false        },        declaration = {          linkSupport = true        },        definition = {          linkSupport = true        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = false        },        implementation = {          linkSupport = true        },        publishDiagnostics = {          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = false,          prepareSupport = true        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = false,          willSaveWaitUntil = false        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = false        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        symbol = {          dynamicRegistration = false,          hierarchicalWorkspaceSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.7.0"    },    initializationOptions = vim.empty_dict(),    processId = 63494,    rootPath = "/tmp/1",    rootUri = "file:///tmp/1",    trace = "off",    workspaceFolders = { {        name = "/tmp/1",        uri = "file:///tmp/1"      } }  }}
[DEBUG][2022-05-08 18:43:57] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      codeActionProvider = {        codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }      },      codeLensProvider = {        resolveProvider = false      },      completionProvider = {        resolveProvider = true,        triggerCharacters = { ".", "#" }      },      declarationProvider = true,      definitionProvider = true,      documentFormattingProvider = true,      documentHighlightProvider = true,      documentSymbolProvider = true,      executeCommandProvider = {        commands = { "dune/promote" }      },      experimental = {        ocamllsp = {          diagnostic_promotions = true,          handleInferIntf = true,          handleSwitchImplIntf = true,          handleTypedHoles = true,          handleWrappingAstNode = true,          interfaceSpecificLangId = true        }      },      foldingRangeProvider = true,      hoverProvider = true,      referencesProvider = true,      renameProvider = {        prepareProvider = true      },      selectionRangeProvider = true,      signatureHelpProvider = {        triggerCharacters = { " ", "~", "?", ":", "(" }      },      textDocumentSync = {        change = 2,        openClose = true,        save = true,        willSave = false,        willSaveWaitUntil = false      },      typeDefinitionProvider = true,      workspace = {        workspaceFolders = {          changeNotifications = true,          supported = true        }      },      workspaceSymbolProvider = true    },    serverInfo = {      name = "ocamllsp",      version = "1.11.4"    }  }}
[DEBUG][2022-05-08 18:43:57] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[DEBUG][2022-05-08 18:43:57] .../lua/vim/lsp.lua:982    "LSP[ocamllsp]" "server_capabilities"   {  codeActionProvider = {    codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }  },  codeLensProvider = {    resolveProvider = false  },  completionProvider = {    resolveProvider = true,    triggerCharacters = { ".", "#" }  },  declarationProvider = true,  definitionProvider = true,  documentFormattingProvider = true,  documentHighlightProvider = true,  documentSymbolProvider = true,  executeCommandProvider = {    commands = { "dune/promote" }  },  experimental = {    ocamllsp = {      diagnostic_promotions = true,      handleInferIntf = true,      handleSwitchImplIntf = true,      handleTypedHoles = true,      handleWrappingAstNode = true,      interfaceSpecificLangId = true    }  },  foldingRangeProvider = true,  hoverProvider = true,  referencesProvider = true,  renameProvider = {    prepareProvider = true  },  selectionRangeProvider = true,  signatureHelpProvider = {    triggerCharacters = { " ", "~", "?", ":", "(" }  },  textDocumentSync = {    change = 2,    openClose = true,    save = true,    willSave = false,    willSaveWaitUntil = false  },  typeDefinitionProvider = true,  workspace = {    workspaceFolders = {      changeNotifications = true,      supported = true    }  },  workspaceSymbolProvider = true}
[INFO][2022-05-08 18:43:57] .../lua/vim/lsp.lua:983 "LSP[ocamllsp]" "initialized"   {  resolved_capabilities = {    call_hierarchy = false,    code_action = {      codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }    },    code_lens = true,    code_lens_resolve = false,    completion = true,    declaration = true,    document_formatting = true,    document_highlight = true,    document_range_formatting = false,    document_symbol = true,    execute_command = true,    find_references = true,    goto_definition = true,    hover = true,    implementation = false,    rename = true,    signature_help = true,    signature_help_trigger_characters = { " ", "~", "?", ":", "(" },    text_document_did_change = 2,    text_document_open_close = true,    text_document_save = true,    text_document_save_include_text = false,    text_document_will_save = false,    text_document_will_save_wait_until = false,    type_definition = true,    workspace_folder_properties = {      changeNotifications = true,      supported = true    },    workspace_symbol = true  }}
[DEBUG][2022-05-08 18:43:57] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "ocaml",      text = 'open Ppxlib\nopen Ast_pattern\n\nlet pat = pstr ((pstr_value nonrecursive ((value_binding ~pat:ppat_any ~expr:__) ^:: nil)) ^:: nil)\n\nlet test =\n  let expand ~loc ~path:_ e =\n    let loc = { loc with loc_ghost = true } in\n    match Sys.getenv_opt "RELEASE" with\n    | Some _ -> Attribute.explicitly_drop#expression e; [%str ]\n    | None -> [%str let () = if Sys.getenv_opt "DO_TEST" <> None then [%e e]]\n  in\n  Extension.declare_inline "ppx.test"\n    Extension.Context.structure_item\n    pat\n    expand\n\nlet () = Driver.register_transformation "ppx" ~extensions:[test]\n',      uri = "file:///tmp/1/ppx.ml",      version = 0    }  }}
[DEBUG][2022-05-08 18:43:58] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = {},    uri = "file:///tmp/1/ppx.ml"  }}
[DEBUG][2022-05-08 18:43:59] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "ocaml",      text = "let%test _ = assert true\n",      uri = "file:///tmp/1/test.ml",      version = 0    }  }}
[DEBUG][2022-05-08 18:43:59] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = {},    uri = "file:///tmp/1/test.ml"  }}
[DEBUG][2022-05-08 18:44:02] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          end = {            character = 0,            line = 1          },          start = {            character = 24,            line = 0          }        },        rangeLength = 1,        text = "\n\n"      }, {        range = {          end = {            character = 0,            line = 1          },          start = {            character = 0,            line = 1          }        },        rangeLength = 0,        text = "  "      }, {        range = {          end = {            character = 2,            line = 1          },          start = {            character = 1,            line = 1          }        },        rangeLength = 1,        text = ""      }, {        range = {          end = {            character = 1,            line = 1          },          start = {            character = 0,            line = 1          }        },        rangeLength = 1,        text = ""      } },    textDocument = {      uri = "file:///tmp/1/test.ml",      version = 6    }  }}
[DEBUG][2022-05-08 18:44:02] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = {},    uri = "file:///tmp/1/test.ml"  }}
[DEBUG][2022-05-08 18:44:03] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          end = {            character = 0,            line = 2          },          start = {            character = 0,            line = 1          }        },        rangeLength = 1,        text = ""      } },    textDocument = {      uri = "file:///tmp/1/test.ml",      version = 8    }  }}
[DEBUG][2022-05-08 18:44:03] .../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"  }}
[DEBUG][2022-05-08 18:44:14] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didSave",  params = {    textDocument = {      uri = "file:///tmp/1/test.ml"    }  }}
[DEBUG][2022-05-08 18:44:14] .../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"  }}
[START][2022-05-08 18:43:47] LSP logging initiated
[INFO][2022-05-08 18:44:16] .../lua/vim/lsp.lua:1392    "exit_handler"  {}
[INFO][2022-05-08 18:44:26] .../lua/vim/lsp.lua:1392    "exit_handler"  { {    _on_attach = <function 1>,    attached_buffers = { true,      [3] = true    },    cancel_request = <function 2>,    commands = {},    config = {      autostart = true,      capabilities = {        callHierarchy = {          dynamicRegistration = false        },        textDocument = {          codeAction = {            codeActionLiteralSupport = {              codeActionKind = {                valueSet = { "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }              }            },            dataSupport = true,            dynamicRegistration = false,            resolveSupport = {              properties = { "edit" }            }          },          completion = {            completionItem = {              commitCharactersSupport = false,              deprecatedSupport = false,              documentationFormat = { "markdown", "plaintext" },              preselectSupport = false,              snippetSupport = false            },            completionItemKind = {              valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }            },            contextSupport = false,            dynamicRegistration = false          },          declaration = {            linkSupport = true          },          definition = {            linkSupport = true          },          documentHighlight = {            dynamicRegistration = false          },          documentSymbol = {            dynamicRegistration = false,            hierarchicalDocumentSymbolSupport = true,            symbolKind = {              valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }            }          },          hover = {            contentFormat = { "markdown", "plaintext" },            dynamicRegistration = false          },          implementation = {            linkSupport = true          },          publishDiagnostics = {            relatedInformation = true,            tagSupport = {              valueSet = { 1, 2 }            }          },          references = {            dynamicRegistration = false          },          rename = {            dynamicRegistration = false,            prepareSupport = true          },          signatureHelp = {            dynamicRegistration = false,            signatureInformation = {              activeParameterSupport = true,              documentationFormat = { "markdown", "plaintext" },              parameterInformation = {                labelOffsetSupport = true              }            }          },          synchronization = {            didSave = true,            dynamicRegistration = false,            willSave = false,            willSaveWaitUntil = false          },          typeDefinition = {            linkSupport = true          }        },        window = {          showDocument = {            support = false          },          showMessage = {            messageActionItem = {              additionalPropertiesSupport = false            }          },          workDoneProgress = true        },        workspace = {          applyEdit = true,          configuration = true,          symbol = {            dynamicRegistration = false,            hierarchicalWorkspaceSymbolSupport = true,            symbolKind = {              valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }            }          },          workspaceEdit = {            resourceOperations = { "rename", "create", "delete" }          },          workspaceFolders = true        }      },      cmd = { "ocamllsp" },      cmd_cwd = "/tmp/1",      cmd_env = {        OCAMLLSP_DEBUG = "1"      },      filetypes = { "ocaml", "ocaml.menhir", "ocaml.interface", "ocaml.ocamllex", "reason" },      flags = {},      get_language_id = <function 3>,      handlers = <1>{},      init_options = vim.empty_dict(),      log_level = 2,      message_level = 2,      name = "ocamllsp",      on_attach = <function 4>,      on_exit = <function 5>,      on_init = <function 6>,      root_dir = "/tmp/1",      settings = vim.empty_dict(),      workspace_folders = <2>{ {          name = "/tmp/1",          uri = "file:///tmp/1"        } },      <metatable> = <3>{        __tostring = <function 7>      }    },    handlers = <table 1>,    id = 1,    initialized = true,    is_stopped = <function 8>,    messages = {      messages = {},      name = "ocamllsp",      progress = {},      status = {}    },    name = "ocamllsp",    notify = <function 9>,    offset_encoding = "utf-16",    request = <function 10>,    request_sync = <function 11>,    requests = {},    resolved_capabilities = {      call_hierarchy = false,      code_action = <4>{        codeActionKinds = { "quickfix", "construct", "destruct", "inferred_intf", "put module name in identifiers", "remove module name from identifiers", "type-annotate" }      },      code_lens = true,      code_lens_resolve = false,      completion = true,      declaration = true,      document_formatting = true,      document_highlight = true,      document_range_formatting = false,      document_symbol = true,      execute_command = true,      find_references = true,      goto_definition = true,      hover = true,      implementation = false,      rename = true,      signature_help = true,      signature_help_trigger_characters = <5>{ " ", "~", "?", ":", "(" },      text_document_did_change = 2,      text_document_open_close = true,      text_document_save = true,      text_document_save_include_text = false,      text_document_will_save = false,      text_document_will_save_wait_until = false,      type_definition = true,      workspace_folder_properties = {        changeNotifications = true,        supported = true      },      workspace_symbol = true    },    rpc = {      handle = <userdata 1>,      notify = <function 12>,      pid = 63502,      request = <function 13>    },    server_capabilities = {      codeActionProvider = <table 4>,      codeLensProvider = {        resolveProvider = false      },      completionProvider = {        resolveProvider = true,        triggerCharacters = { ".", "#" }      },      declarationProvider = true,      definitionProvider = true,      documentFormattingProvider = true,      documentHighlightProvider = true,      documentSymbolProvider = true,      executeCommandProvider = {        commands = { "dune/promote" }      },      experimental = {        ocamllsp = {          diagnostic_promotions = true,          handleInferIntf = true,          handleSwitchImplIntf = true,          handleTypedHoles = true,          handleWrappingAstNode = true,          interfaceSpecificLangId = true        }      },      foldingRangeProvider = true,      hoverProvider = true,      referencesProvider = true,      renameProvider = {        prepareProvider = true      },      selectionRangeProvider = true,      signatureHelpProvider = {        triggerCharacters = <table 5>      },      textDocumentSync = {        change = 2,        openClose = true,        save = true,        willSave = false,        willSaveWaitUntil = false      },      typeDefinitionProvider = true,      workspace = {        workspaceFolders = {          changeNotifications = true,          supported = true        }      },      workspaceSymbolProvider = true    },    stop = <function 14>,    supports_method = <function 15>,    workspaceFolders = <table 2>,    workspace_did_change_configuration = <function 16>,    workspace_folders = <table 2>  } }
[DEBUG][2022-05-08 18:44:26] .../vim/lsp/rpc.lua:347    "rpc.send"  {  id = 2,  jsonrpc = "2.0",  method = "shutdown"}
[DEBUG][2022-05-08 18:44:26] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  id = 2,  jsonrpc = "2.0"}
[DEBUG][2022-05-08 18:44:26] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "exit"}
copy commented 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?

rgrinberg commented 2 years ago

One thing that would help narrow this down: is it an issue with regular merlin?

copy commented 2 years ago

No, with regular merlin the issue doesn't happen.

copy commented 2 years ago

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\")
rgrinberg commented 2 years ago

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"
rgrinberg commented 2 years ago

In the vendored submodule of merlin in ocamllsp

rgrinberg commented 2 years ago

Actually, never mind. I think I understand the issue. The scheduler is calling waitpid and that's probably reaping it for the ppx.

rgrinberg commented 2 years ago

Try #735 and let me know if it fixes the issue.

copy commented 2 years ago

I can confirm that #735 fixes the issue for me.