Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing and encoding JSON booleans doesn't roundtrip correctly for code actions #4184

Closed
3 tasks done
robbert-vdh opened this issue Oct 1, 2023 · 4 comments · Fixed by #4194 or emacs-lsp/lsp-haskell#183
Closed
3 tasks done
Labels

Comments

@robbert-vdh
Copy link
Contributor

robbert-vdh commented Oct 1, 2023

Original title: Argument parsing errors when using the 'Add placeholders for ...' code actions in haskell-language-server

Thank you for the bug report

  • I am using the latest version of lsp-mode related packages.
  • I checked FAQ and Troubleshooting sections
  • You may also try reproduce the issue using clean environment using the following command: M-x lsp-start-plain

Bug description

This has been happening for at least a year now. I was silently hoping this would magically get fixed in the meantime (on either the language server's or lsp-mode's side), but I guess that didn't happen. 😄

Also wasn't quite whether to file this under lsp-mode's or lsp-haskell's issue tracker, but since this involves a parsing issue I posted it here.

Using any of the Add placeholders for code actions from Haskell Language Server's class actions plugin results in the following error:

(error "Error while parsing args for classplugin.codeaction in plugin class: expected Bool, but encountered Null, arg = Object (fromList [(\"methodGroup\",Array [Array [String \"show\",String \"show :: Foo -> String\"]]),(\"range\",Object (fromList [(\"end\",Object (fromList [(\"character\",Number 17.0),(\"line\",Number 2.0)])),(\"start\",Object (fromList [(\"character\",Number 9.0),(\"line\",Number 2.0)]))])),(\"verTxtDocId\",Object (fromList [(\"uri\",String \"file:///tmp/foo/Foo.hs\"),(\"version\",Number 44.0)])),(\"withSig\",Null)])")

The issue has previously been reported on hls' issue tracker here: haskell/haskell-language-server#2993

Steps to reproduce

This happens any time you try to execute one of these code actions. The simplest way to reproduce this would be to create a .hs file with the following contents:

data Foo

instance Show Foo where

Then hover over the 'No implicit implementation for' warning at Show Foo and try to execute any of the placeholder code actions.

Expected behavior

I'd expect the placeholders to be inserted.

Which Language Server did you use?

This happens with any HLS version. I recently used 1.8.0.0 up to 2.3.0.0 that all had this issue.

OS

Linux

Error callstack

Backtrace
Debugger entered--Lisp error: (error "Error while parsing args for classplugin.codeactio...")
  error("Error while parsing args for classplugin.codeactio...")
  lsp-request("workspace/executeCommand" (:command "128453:class:classplugin.codeaction" :arguments [#<hash-table equal 4/4 0x1d0616d>]))
  lsp-workspace-command-execute("128453:class:classplugin.codeaction" [#<hash-table equal 4/4 0x1d0616d>])
  lsp-send-execute-command("128453:class:classplugin.codeaction" [#<hash-table equal 4/4 0x1d0616d>])
  lsp--execute-command(#<hash-table equal 3/3 0x1d06117>)
  lsp--execute-code-action(#<hash-table equal 4/4 0x1d060cd>)
  lsp-execute-code-action(#<hash-table equal 4/4 0x1d060cd>)
  funcall-interactively(lsp-execute-code-action #<hash-table equal 4/4 0x1d060cd>)
  command-execute(lsp-execute-code-action)
*lsp-log*
Command "haskell-language-server-wrapper --lsp -l /tmp/hls.log" is present on the path.
Command "haskell-language-server-wrapper --lsp -l /tmp/hls.log" is present on the path.
Found the following clients for /tmp/foo/Foo.hs: (server-id lsp-haskell, priority 0)
The following clients were selected based on priority: (server-id lsp-haskell, priority 0)
Creating watchers for following 1 folders:
  /tmp/foo
Cancelling textDocument/codeAction(7) in hook post-command-hook
*lsp-log: lsp-haskell:128453*
[Trace - 12:21:07 ] Sending request 'textDocument/hover - (6)'.
Params: {
  "textDocument": {
    "uri": "file:///tmp/foo/Foo.hs"
  },
  "position": {
    "line": 2,
    "character": 9
  }
}


[Trace - 12:21:07 ] Sending request 'textDocument/codeAction - (7)'.
Params: {
  "textDocument": {
    "uri": "file:///tmp/foo/Foo.hs"
  },
  "range": {
    "start": {
      "line": 2,
      "character": 9
    },
    "end": {
      "line": 2,
      "character": 9
    }
  },
  "context": {
    "diagnostics": [
      {
        "code": "-Wmissing-methods",
        "message": "• No explicit implementation for\n    either ‘showsPrec’ or ‘show’\n• In the instance declaration for ‘Show Foo’",
        "range": {
          "end": {
            "character": 17,
            "line": 2
          },
          "start": {
            "character": 9,
            "line": 2
          }
        },
        "severity": 2,
        "source": "typecheck"
      }
    ]
  }
}


[Trace - 12:21:07 ] Received response 'textDocument/hover - (6)' in 1ms.
Result: {
  "contents": {
    "kind": "markdown",
    "value": "\n\nShow\n```\n\n*Defined in ‘GHC.Show’* *(base-4.17.2.0)*\n\n"
  },
  "range": {
    "end": {
      "character": 13,
      "line": 2
    },
    "start": {
      "character": 9,
      "line": 2
    }
  }
}


[Trace - 12:21:08 ] Sending notification '$/cancelRequest'.
Params: {
  "id": 7
}


[Trace - 12:21:08 ] Received response 'nil - (7)' in 0ms.
Result: [
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'showsPrec'"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'showsPrec'"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'showsPrec' with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'showsPrec' with signature(s)"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "show",
              "show :: Foo -> String"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'show'"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'show'"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "show",
              "show :: Foo -> String"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'show' with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'show' with signature(s)"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ],
            [
              "show",
              "show :: Foo -> String"
            ],
            [
              "showList",
              "showList :: [Foo] -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for all missing methods"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for all missing methods"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ],
            [
              "show",
              "show :: Foo -> String"
            ],
            [
              "showList",
              "showList :: [Foo] -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for all missing methods with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for all missing methods with signature(s)"
  },
  {
    "diagnostics": [],
    "edit": {
      "changes": {
        "file:///tmp/foo/Foo.hs": [
          {
            "newText": "{-# OPTIONS_GHC -Wno-missing-methods #-}\n",
            "range": {
              "end": {
                "character": 0,
                "line": 0
              },
              "start": {
                "character": 0,
                "line": 0
              }
            }
          }
        ]
      }
    },
    "kind": "quickfix",
    "title": "Disable \"missing-methods\" warnings"
  }
]


[Trace - 12:21:09 ] Sending request 'textDocument/codeAction - (8)'.
Params: {
  "textDocument": {
    "uri": "file:///tmp/foo/Foo.hs"
  },
  "range": {
    "start": {
      "line": 2,
      "character": 9
    },
    "end": {
      "line": 2,
      "character": 9
    }
  },
  "context": {
    "diagnostics": [
      {
        "code": "-Wmissing-methods",
        "message": "• No explicit implementation for\n    either ‘showsPrec’ or ‘show’\n• In the instance declaration for ‘Show Foo’",
        "range": {
          "end": {
            "character": 17,
            "line": 2
          },
          "start": {
            "character": 9,
            "line": 2
          }
        },
        "severity": 2,
        "source": "typecheck"
      }
    ]
  }
}


[Trace - 12:21:09 ] Received response 'textDocument/codeAction - (8)' in 7ms.
Result: [
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'showsPrec'"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'showsPrec'"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'showsPrec' with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'showsPrec' with signature(s)"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "show",
              "show :: Foo -> String"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'show'"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'show'"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "show",
              "show :: Foo -> String"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'show' with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'show' with signature(s)"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ],
            [
              "show",
              "show :: Foo -> String"
            ],
            [
              "showList",
              "showList :: [Foo] -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for all missing methods"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for all missing methods"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ],
            [
              "show",
              "show :: Foo -> String"
            ],
            [
              "showList",
              "showList :: [Foo] -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for all missing methods with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for all missing methods with signature(s)"
  },
  {
    "diagnostics": [],
    "edit": {
      "changes": {
        "file:///tmp/foo/Foo.hs": [
          {
            "newText": "{-# OPTIONS_GHC -Wno-missing-methods #-}\n",
            "range": {
              "end": {
                "character": 0,
                "line": 0
              },
              "start": {
                "character": 0,
                "line": 0
              }
            }
          }
        ]
      }
    },
    "kind": "quickfix",
    "title": "Disable \"missing-methods\" warnings"
  }
]


[Trace - 12:21:09 ] Sending request 'textDocument/codeAction - (9)'.
Params: {
  "textDocument": {
    "uri": "file:///tmp/foo/Foo.hs"
  },
  "range": {
    "start": {
      "line": 2,
      "character": 9
    },
    "end": {
      "line": 2,
      "character": 9
    }
  },
  "context": {
    "diagnostics": [
      {
        "code": "-Wmissing-methods",
        "message": "• No explicit implementation for\n    either ‘showsPrec’ or ‘show’\n• In the instance declaration for ‘Show Foo’",
        "range": {
          "end": {
            "character": 17,
            "line": 2
          },
          "start": {
            "character": 9,
            "line": 2
          }
        },
        "severity": 2,
        "source": "typecheck"
      }
    ]
  }
}


[Trace - 12:21:09 ] Received response 'textDocument/codeAction - (9)' in 7ms.
Result: [
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'showsPrec'"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'showsPrec'"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'showsPrec' with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'showsPrec' with signature(s)"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "show",
              "show :: Foo -> String"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'show'"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'show'"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "show",
              "show :: Foo -> String"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for 'show' with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for 'show' with signature(s)"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ],
            [
              "show",
              "show :: Foo -> String"
            ],
            [
              "showList",
              "showList :: [Foo] -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": null
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for all missing methods"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for all missing methods"
  },
  {
    "command": {
      "arguments": [
        {
          "methodGroup": [
            [
              "showsPrec",
              "showsPrec :: Int -> Foo -> ShowS"
            ],
            [
              "show",
              "show :: Foo -> String"
            ],
            [
              "showList",
              "showList :: [Foo] -> ShowS"
            ]
          ],
          "range": {
            "end": {
              "character": 17,
              "line": 2
            },
            "start": {
              "character": 9,
              "line": 2
            }
          },
          "verTxtDocId": {
            "uri": "file:///tmp/foo/Foo.hs",
            "version": 0
          },
          "withSig": true
        }
      ],
      "command": "128453:class:classplugin.codeaction",
      "title": "Add placeholders for all missing methods with signature(s)"
    },
    "diagnostics": [],
    "kind": "quickfix",
    "title": "Add placeholders for all missing methods with signature(s)"
  },
  {
    "diagnostics": [],
    "edit": {
      "changes": {
        "file:///tmp/foo/Foo.hs": [
          {
            "newText": "{-# OPTIONS_GHC -Wno-missing-methods #-}\n",
            "range": {
              "end": {
                "character": 0,
                "line": 0
              },
              "start": {
                "character": 0,
                "line": 0
              }
            }
          }
        ]
      }
    },
    "kind": "quickfix",
    "title": "Disable \"missing-methods\" warnings"
  }
]


[Trace - 12:21:11 ] Sending request 'workspace/executeCommand - (10)'.
Params: {
  "command": "128453:class:classplugin.codeaction",
  "arguments": [
    {
      "methodGroup": [
        [
          "show",
          "show :: Foo -> String"
        ]
      ],
      "range": {
        "end": {
          "character": 17,
          "line": 2
        },
        "start": {
          "character": 9,
          "line": 2
        }
      },
      "verTxtDocId": {
        "uri": "file:///tmp/foo/Foo.hs",
        "version": 0
      },
      "withSig": null
    }
  ]
}


[Trace - 12:21:11 ] Received response 'workspace/executeCommand - (10)' in 0ms.
Result: {
  "code": -32602,
  "message": "Error while parsing args for classplugin.codeaction in plugin class: expected Bool, but encountered Null, arg = Object (fromList [(\"methodGroup\",Array [Array [String \"show\",String \"show :: Foo -> String\"]]),(\"range\",Object (fromList [(\"end\",Object (fromList [(\"character\",Number 17.0),(\"line\",Number 2.0)])),(\"start\",Object (fromList [(\"character\",Number 9.0),(\"line\",Number 2.0)]))])),(\"verTxtDocId\",Object (fromList [(\"uri\",String \"file:///tmp/foo/Foo.hs\"),(\"version\",Number 0.0)])),(\"withSig\",Null)])"
}
*lsp-haskell::stderr*
No 'hie.yaml' found. Try to discover the project type!
Run entered for haskell-language-server-wrapper(haskell-language-server-wrapper) Version 2.3.0.0 x86_64 ghc-9.0.2
Current directory: /tmp/foo
Operating system: linux
Arguments: ["--lsp","-l","/tmp/hls.log"]
Cradle directory: /tmp/foo
Cradle type: Default

Tool versions found on the $PATH
cabal:          3.10.1.0
stack:          2.7.5
ghc:            9.4.7


Consulting the cradle to get project GHC version...
2023-10-01T10:21:05.166936Z | Debug | executing command: ghc --numeric-version
Project GHC version: 9.4.7
haskell-language-server exe candidates: ["haskell-language-server-9.4.7","haskell-language-server"]
Launching haskell-language-server exe at:/home/robbert/.ghcup/bin/haskell-language-server-9.4.7
2023-10-01T10:21:05.194101Z | Debug | executing command: ghc -v0 -package-env=- -ignore-dot-ghci -e Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)
2023-10-01T10:21:05.273221Z | Debug | executing command: ghc --print-libdir
2023-10-01T10:21:06.284139Z | Info | haskell-language-server version: 2.3.0.0 (GHC: 9.4.7) (PATH: /home/robbert/.ghcup/hls/2.3.0.0/lib/haskell-language-server-2.3.0.0/bin/haskell-language-server-9.4.7)
2023-10-01T10:21:06.284524Z | Info | Directory: /tmp/foo
2023-10-01T10:21:06.284727Z | Info | Starting (haskell-language-server) LSP server...
  GhcideArguments {argsCommand = LSP, argsCwd = Nothing, argsShakeProfiling = Nothing, argsTesting = False, argsExamplePlugin = False, argsLogLevel = Info, argsLogFile = Just "/tmp/hls.log", argsLogStderr = True, argsLogClient = False, argsThreads = 0, argsProjectGhcVersion = False}
  PluginIds: [ pragmas-suggest
             , pragmas-completion
             , retrie
             , ghcide-completions
             , alternateNumberFormat
             , ghcide-code-actions-bindings
             , explicit-fields
             , ghcide-hover-and-symbols
             , ghcide-code-actions-type-signatures
             , floskell
             , fourmolu
             , ghcide-extend-import-action
             , ghcide-code-actions-fill-holes
             , importLens
             , LSPRecorderCallback
             , cabal
             , qualifyImportedNames
             , moduleName
             , splice
             , stylish-haskell
             , changeTypeSignature
             , hlint
             , class
             , ormolu
             , callHierarchy
             , ghcide-type-lenses
             , codeRange
             , cabal-fmt
             , eval
             , rename
             , ghcide-code-actions-imports-exports
             , gadt
             , overloaded-record-dot
             , ghcide-core
             , explicit-fixity
             , pragmas-disable ]
2023-10-01T10:21:06.286618Z | Info | Logging heap statistics every 60.00s
 2023-10-01T10:21:06.302990Z | Info | Starting LSP server...
  If you are seeing this in a terminal, you probably should have run WITHOUT the --lsp option!
  PluginIds: [ pragmas-suggest
             , pragmas-completion
             , retrie
             , ghcide-completions
             , alternateNumberFormat
             , ghcide-code-actions-bindings
             , explicit-fields
             , ghcide-hover-and-symbols
             , ghcide-code-actions-type-signatures
             , floskell
             , fourmolu
             , ghcide-extend-import-action
             , ghcide-code-actions-fill-holes
             , importLens
             , LSPRecorderCallback
             , cabal
             , qualifyImportedNames
             , moduleName
             , splice
             , stylish-haskell
             , changeTypeSignature
             , hlint
             , class
             , ormolu
             , callHierarchy
             , ghcide-type-lenses
             , codeRange
             , cabal-fmt
             , eval
             , rename
             , ghcide-code-actions-imports-exports
             , gadt
             , overloaded-record-dot
             , ghcide-core
             , explicit-fixity
             , pragmas-disable ]
2023-10-01T10:21:06.303284Z | Info | Starting server
2023-10-01T10:21:06.304382Z | Warning | LSP: configuration parse error:
parsing settings failed, expected Object, but encountered Null
when parsing
null
2023-10-01T10:21:06.304803Z | Info | Started LSP server in 0.00s
2023-10-01T10:21:06.337587Z | Info | Registering IDE configuration: IdeConfiguration {workspaceFolders = fromList [NormalizedUri (-6961054525489253453) "file:///tmp/foo"], clientSettings = hashed (Just Null)}
2023-10-01T10:21:06.357826Z | Info | Cradle path: Foo.hs
2023-10-01T10:21:06.357895Z | Warning | No [cradle](https://github.com/mpickering/hie-bios#hie-bios) found for Foo.hs.
Proceeding with [implicit cradle](https://hackage.haskell.org/package/implicit-hie).
You should ignore this message, unless you see a 'Multi Cradle: No prefixes matched' error.
2023-10-01T10:21:06.360657Z | Info | invoking build tool to determine build flags (this may take some time depending on the cache)
2023-10-01T10:21:06.417009Z | Info | Interface files cache directory: /home/robbert/.cache/ghcide/main-1a596a151463f2c53ee4feb14ecd276a1ccebfda-1a596a151463f2c53ee4feb14ecd276a1ccebfda
2023-10-01T10:21:06.417104Z | Info | Making new HscEnv. In-place unit ids: [ main-1a596a151463f2c53ee4feb14ecd276a1ccebfda ]
2023-10-01T10:21:07.931325Z | Info | class: Detected implemented methods for class "Show": []
2023-10-01T10:21:09.633401Z | Info | class: Detected implemented methods for class "Show": []
2023-10-01T10:21:09.763443Z | Info | class: Detected implemented methods for class "Show": []
2023-10-01T10:21:11.322157Z | Warning | class: {
  "code": -32602,
  "message": "Error while parsing args for classplugin.codeaction in plugin class: expected Bool, but encountered Null, arg = Object (fromList [(\"methodGroup\",Array [Array [String \"show\",String \"show :: Foo -> String\"]]),(\"range\",Object (fromList [(\"end\",Object (fromList [(\"character\",Number 17.0),(\"line\",Number 2.0)])),(\"start\",Object (fromList [(\"character\",Number 9.0),(\"line\",Number 2.0)]))])),(\"verTxtDocId\",Object (fromList [(\"uri\",String \"file:///tmp/foo/Foo.hs\"),(\"version\",Number 0.0)])),(\"withSig\",Null)])"
}

Anything else?

No response

@robbert-vdh robbert-vdh added the bug label Oct 1, 2023
@robbert-vdh
Copy link
Contributor Author

robbert-vdh commented Oct 13, 2023

So the problem here is that json-parse-buffer translates false to nil, and nil encodes back to JSON as null. Those "withSig": null entries should be "withSig": false.

It parses this JSON:

"{\"id\":74,\"jsonrpc\":\"2.0\",\"result\":[{\"command\":{\"arguments\":[{\"methodGroup\":[[\"show\",\"show :: Foo -> String\"]],\"range\":{\"end\":{\"character\":17,\"line\":7},\"start\":{\"character\":9,\"line\":7}},\"verTxtDocId\":{\"uri\":\"file:///home/robbert/Documents/forks/lsp-mode/Foo.hs\",\"version\":2},\"withSig\":false}],\"command\":\"1065989:class:classplugin.codeaction\",\"title\":\"Add placeholders for 'show'\"},\"diagnostics\":[],\"kind\":\"quickfix\",\"title\":\"Add placeholders for 'show'\"},{\"command\":{\"arguments\":[{\"methodGroup\":[[\"show\",\"show :: Foo -> String\"]],\"range\":{\"end\":{\"character\":17,\"line\":7},\"start\":{\"character\":9,\"line\":7}},\"verTxtDocId\":{\"uri\":\"file:///home/robbert/Documents/forks/lsp-mode/Foo.hs\",\"version\":2},\"withSig\":true}],\"command\":\"1065989:class:classplugin.codeaction\",\"title\":\"Add placeholders for 'show' with signature(s)\"},\"diagnostics\":[],\"kind\":\"quickfix\",\"title\":\"Add placeholders for 'show' with signature(s)\"},{\"command\":{\"arguments\":[{\"methodGroup\":[[\"show\",\"show :: Foo -> String\"],[\"showList\",\"showList :: [Foo] -> ShowS\"]],\"range\":{\"end\":{\"character\":17,\"line\":7},\"start\":{\"character\":9,\"line\":7}},\"verTxtDocId\":{\"uri\":\"file:///home/robbert/Documents/forks/lsp-mode/Foo.hs\",\"version\":2},\"withSig\":false}],\"command\":\"1065989:class:classplugin.codeaction\",\"title\":\"Add placeholders for all missing methods\"},\"diagnostics\":[],\"kind\":\"quickfix\",\"title\":\"Add placeholders for all missing methods\"},{\"command\":{\"arguments\":[{\"methodGroup\":[[\"show\",\"show :: Foo -> String\"],[\"showList\",\"showList :: [Foo] -> ShowS\"]],\"range\":{\"end\":{\"character\":17,\"line\":7},\"start\":{\"character\":9,\"line\":7}},\"verTxtDocId\":{\"uri\":\"file:///home/robbert/Documents/forks/lsp-mode/Foo.hs\",\"version\":2},\"withSig\":true}],\"command\":\"1065989:class:classplugin.codeaction\",\"title\":\"Add placeholders for all missing methods with signature(s)\"},\"diagnostics\":[],\"kind\":\"quickfix\",\"title\":\"Add placeholders for all missing methods with signature(s)\"},{\"diagnostics\":[],\"edit\":{\"changes\":{\"file:///home/robbert/Documents/forks/lsp-mode/Foo.hs\":[{\"newText\":\"{-# OPTIONS_GHC -Wno-missing-methods #-}\\n\",\"range\":{\"end\":{\"character\":0,\"line\":0},\"start\":{\"character\":0,\"line\":0}}}]}},\"kind\":\"quickfix\",\"title\":\"Disable \\\"missing-methods\\\" warnings\"}]}"

To this:

#s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("id" 74 "jsonrpc" "2.0" "result" [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("arguments" [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("methodGroup" [["show" "show :: Foo -> String"]] "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 17 "line" 7)) "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 9 "line" 7)))) "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2)) "withSig" nil))] "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for 'show'")) "diagnostics" [] "kind" "quickfix" "title" "Add placeholders for 'show'")) #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("arguments" [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("methodGroup" [["show" "show :: Foo -> String"]] "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 17 "line" 7)) "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 9 "line" 7)))) "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2)) "withSig" t))] "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for 'show' with signature(s)")) "diagnostics" [] "kind" "quickfix" "title" "Add placeholders for 'show' with signature(s)")) #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("arguments" [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("methodGroup" [["show" "show :: Foo -> String"] ["showList" "showList :: [Foo] -> ShowS"]] "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 17 "line" 7)) "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 9 "line" 7)))) "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2)) "withSig" nil))] "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for all missing methods")) "diagnostics" [] "kind" "quickfix" "title" "Add placeholders for all missing methods")) #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("arguments" [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("methodGroup" [["show" "show :: Foo -> String"] ["showList" "showList :: [Foo] -> ShowS"]] "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 17 "line" 7)) "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 9 "line" 7)))) "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2)) "withSig" t))] "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for all missing methods with signature(s)")) "diagnostics" [] "kind" "quickfix" "title" "Add placeholders for all missing methods with signature(s)")) #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("diagnostics" [] "edit" #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("changes" #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" [#s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("newText" "{-# OPTIONS_GHC -Wno-missing-methods #-}
" "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 0 "line" 0)) "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 0 "line" 0))))))])))) "kind" "quickfix" "title" "Disable \"missing-methods\" warnings"))]))

However, throwing this same input into json-parse-string yields this instead:

#s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data
              ("id" 74 "jsonrpc" "2.0" "result"
               [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                              ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                       ("arguments"
                                                        [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                       ("methodGroup"
                                                                        [["show" "show :: Foo -> String"]]
                                                                        "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                              ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                   ("character" 17 "line" 7))
                                                                                               "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                     ("character" 9 "line" 7))))
                                                                        "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                    ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2))
                                                                        "withSig" :false))]
                                                        "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for 'show'"))
                               "diagnostics"
                               []
                               "kind" "quickfix" "title" "Add placeholders for 'show'"))
                  #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                         ("arguments"
                                                          [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                         ("methodGroup"
                                                                          [["show" "show :: Foo -> String"]]
                                                                          "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                     ("character" 17 "line" 7))
                                                                                                 "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                       ("character" 9 "line" 7))))
                                                                          "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                      ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2))
                                                                          "withSig" t))]
                                                          "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for 'show' with signature(s)"))
                                 "diagnostics"
                                 []
                                 "kind" "quickfix" "title" "Add placeholders for 'show' with signature(s)"))
                  #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                         ("arguments"
                                                          [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                         ("methodGroup"
                                                                          [["show" "show :: Foo -> String"]
                                                                           ["showList" "showList :: [Foo] -> ShowS"]]
                                                                          "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                     ("character" 17 "line" 7))
                                                                                                 "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                       ("character" 9 "line" 7))))
                                                                          "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                      ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2))
                                                                          "withSig" :false))]
                                                          "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for all missing methods"))
                                 "diagnostics"
                                 []
                                 "kind" "quickfix" "title" "Add placeholders for all missing methods"))
                  #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                ("command" #s(hash-table size 3 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                         ("arguments"
                                                          [#s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                         ("methodGroup"
                                                                          [["show" "show :: Foo -> String"]
                                                                           ["showList" "showList :: [Foo] -> ShowS"]]
                                                                          "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                     ("character" 17 "line" 7))
                                                                                                 "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                                       ("character" 9 "line" 7))))
                                                                          "verTxtDocId" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                                      ("uri" "file:///home/robbert/Documents/forks/lsp-mode/Foo.hs" "version" 2))
                                                                          "withSig" t))]
                                                          "command" "1065989:class:classplugin.codeaction" "title" "Add placeholders for all missing methods with signature(s)"))
                                 "diagnostics"
                                 []
                                 "kind" "quickfix" "title" "Add placeholders for all missing methods with signature(s)"))
                  #s(hash-table size 4 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                ("diagnostics"
                                 []
                                 "edit" #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                      ("changes" #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                               ("file:///home/robbert/Documents/forks/lsp-mode/Foo.hs"
                                                                                [#s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                                                                               ("newText" "{-# OPTIONS_GHC -Wno-missing-methods #-}
" "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                        ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                             ("character" 0 "line" 0))
                         "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data
                                               ("character" 0 "line" 0))))))]))))
                                 "kind" "quickfix" "title" "Disable \"missing-methods\" warnings"))]))

@robbert-vdh
Copy link
Contributor Author

Oh, yes, of course it does this. This is how lsp-mode invokes json-parse-buffer:

(json-parse-buffer :object-type (if lsp-use-plists
                                    'plist
                                  'hash-table)
 :null-object nil
 :false-object nil)

It doesn't distinguish between false and null. I'm surprised this doesn't cause more issues.

robbert-vdh added a commit to robbert-vdh/lsp-mode that referenced this issue Oct 13, 2023
This would break code actions where the server sends us an action
containing a `false` boolean parameter. `lsp-mode` would translate this
into `nil`, and then send a `null` back to the language server. An
example of this causing problems was
emacs-lsp#4184.

There may be parts of `lsp-mode` that expect `false` values to be parsed
as `nil`, so any logic involving parsed booleans may need to be updated.
@robbert-vdh robbert-vdh changed the title Argument parsing errors when using the 'Add placeholders for ...' code actions in haskell-language-server Parsing and encoding JSON booleans doesn't roundtrip correctly for code actions Oct 13, 2023
robbert-vdh added a commit to robbert-vdh/lsp-mode that referenced this issue Oct 14, 2023
This lets us fix emacs-lsp#4184 by selectively changing `nil` code action
argument values to `:json-false`. Once this is merged, HLS needs to set
the field to `'(:withSig)` (and possibly some other values).
robbert-vdh added a commit to robbert-vdh/lsp-mode that referenced this issue Oct 30, 2023
This lets us fix emacs-lsp#4184 by selectively changing `nil` code action
argument values to `:json-false`. Once this is merged, HLS needs to set
the field to `'(:withSig)` (and possibly some other values).
robbert-vdh added a commit to robbert-vdh/lsp-mode that referenced this issue Jun 9, 2024
This lets us fix emacs-lsp#4184 by selectively changing `nil` code action
argument values to `:json-false`. Once this is merged, HLS needs to set
the field to `'(:withSig)` (and possibly some other values).
yyoncho pushed a commit that referenced this issue Jun 10, 2024
This lets us fix #4184 by selectively changing `nil` code action
argument values to `:json-false`. Once this is merged, HLS needs to set
the field to `'(:withSig)` (and possibly some other values).
@michaelpj
Copy link
Contributor

Hmm. We already try and be a bit permissive on the HLS side as various clients don't follow the spec. Particularly in terms of using null instead of absent/empty list/false. We accept null to mean "absent", perhaps we could also accept it to mean "false".

@robbert-vdh
Copy link
Contributor Author

I think in most cases it wouldn't hurt to treat\ null values as false for the sake of client compatibility! Though we'd probably still need these changes from emacs-lsp/lsp-haskell#183 regardless for compatibility with older versions of HLS. That may sometimes still be necessary when for instance working with a version of GHC no longer supported by the current version of HLS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment