haskell / haskell-language-server

Official haskell ide support via language server (LSP). Successor of ghcide & haskell-ide-engine.
Apache License 2.0
2.72k stars 367 forks source link

hlint misses -XNoForeignFunctionInterface in the cradle #3095

Open lf- opened 2 years ago

lf- commented 2 years ago

Here is an hls branch with a broken test case (only runs on ghc-9.2):

https://github.com/lf-/haskell-language-server/tree/hlint-flags-bug

and the commit, for convenience: https://github.com/lf-/haskell-language-server/commit/8a90d27bc108695911a1c767acd615dba03e1791

The issue happens in this source:

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Foo (Node(..)) where

data Node = Node
  {
    label :: ()
  }

instance Semigroup Node where
  n1 <> n2 = Node
    { label = n1.label <> n2.label
    --            ^^^ this is a parse error because "label" is undocumentedly a reserved word when -XForeignFunctionInterface is enabled (which it is by default)
    }

The bug is that hls' hlint plugin will trigger parse errors, even if the hlint.yaml has -XNoForeignFunctionInterface in it, and also even if the cradle provides it.

It is "fixable" in a project by "haskell.plugin.hlint.config.flags": ["-XNoForeignFunctionInterface"].

The cause of this bug is that the disabled language extensions are not correctly propagated to hlint when hlint is called: only enabled extensions are propagated:

https://github.com/haskell/haskell-language-server/blob/1bc1def498c69681cef934c9b1d9cbb460591788/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs#L318-L321

Unfortunately, actually retrieving disabled extensions is significantly frustrated by OnOff (https://hackage.haskell.org/package/ghc-9.2.4/docs/src/GHC.Driver.Session.html#OnOff) being a private member of GHC.Driver.Session, so it is impossible to get the disabled extensions out of DynFlags.extensions without more or less doing something like read . dropOnOffPrefix . show.

So what I am 99% sure is happening, based on putting traces in both sides, is that ForeignFunctionInterface is absent from the enabledExtensions passed to hlint if the cradle is configured with -XNoForeignFunctionInterface, but it is then reenabled because it is a default extension.

I got fairly far in fixing this, but ran up against this brick wall, and don't really have more time to commit to fixing it. So I am filing this report with the hopes that it will at least be easier to fix. I found some time :)

Your environment

..

Steps to reproduce

Run the test suite from https://github.com/lf-/haskell-language-server/tree/hlint-flags-bug

Expected behaviour

Should pass :)

Actual behaviour

      src/Test/Hls/Util.hs:317:
      Got unexpected diagnostics for Uri {getUri = "file:///Users/jade/co/haskell-language-server/plugins/hls-hlint-plugin/test/testdata/lab
elkeyword/LabelKeyword.hs"} got [Diagnostic {_range = Range {_start = Position {_line = 11, _character = 17}, _end = Position {_line = 11, _
character = 22}}, _severity = Just DsInfo, _code = Just (InR "parser"), _source = Just "hlint", _message = "/Users/jade/co/haskell-language-
server/plugins/hls-hlint-plugin/test/testdata/labelkeyword/LabelKeyword.hs:12:18: error:\n    parse error on input `label'\n  instance Semig
roup Node where\n    n1 <> n2 = Node\n>     { label = n1.label <> n2.label\n      }\n\n", _tags = Nothing, _relatedInformation = Nothing}]

Debug information

lf- commented 2 years ago

https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8817

i have filed a ghc MR to fix the export issue but it obviously would have to get released so it's a long term solution i guess

lf- commented 2 years ago

Just found out that the Fourmolu provider also contains this bug or a very similar one, as calling format in my editor causes a similar error to be logged but fourmolu from the command line is fine.