haskell / haskell/haskell-language-server

hlint misses -XNoForeignFunctionInterface in the cradle

Open
#3,095 2 comments 1 reaction 0 assignees View on GitHub
component: formatters component: hls-hlint-plugin status: blocked type: bug
Dominant language
Haskell
Stars
3k
Forks
455
Avg merge
2d 19h
Merged PRs (30d)
11

Description

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:

```haskell
{-# 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

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.