haskell-language-server icon indicating copy to clipboard operation
haskell-language-server copied to clipboard

Ormolu/Fourmolu incorrectly infers `GHC2021` in Stack projects

Open keithfancher opened this issue 2 years ago • 9 comments

Summary: Formatting via HLS moves my qualified keywords as if I had the ImportQualifiedPost plugin enabled, which I do not. Interestingly, this bug only seems to occur in projects using GHC 9.2.5?

Note that I've tried the standalone ormolu (0.5.1) and this problem does not occur. I can't figure out how to check which version of ormolu my HLS is using, however...

Your environment

Which OS do you use?

Ubuntu 20.04

Which version of GHC do you use and how did you install it?

9.2.5, installed via stack.

NOTE: I tried this in an older project, using GHC 9.0.2 (also via stack), and the bug did NOT occur.

How is your project built (alternative: link to the project)?

stack

Which LSP client (editor/plugin) do you use?

Tried in neovim 0.8.2 and vs code 1.74.2 with same result.

Which version of HLS do you use and how did you install it?

HLS 1.9.0, via ghcup.

Have you configured HLS in any way (especially: a hie.yaml file)?

No extra configs

Steps to reproduce

  1. Verify that ImportQualifiedPost is not enabled anywhere in the project
  2. Add a qualified import
  3. Use HLS to format the file

Expected behaviour

The following import should remain as-is:

import qualified Data.Text as T

Actual behaviour

The import is altered, qualified is moved:

import Data.Text qualified as T

Debug information

N/A

keithfancher avatar Jan 05 '23 06:01 keithfancher

Ah, I wonder if this is related to GHC2021! That includes ImportQualifiedPost.

From the docs:

GHC2021 is used by GHC if neither Haskell98 nor Haskell2010 is turned on explicitly.

The only extension I have enabled explicitly in this project is OverloadedStrings. Does this mean that the extension is enabled implicitly? Though if that were the case, I would expect the project to build. However, after formatting the file:

$ stack build
kept-0.1.0.0: unregistering (local file changes: src/Parse.hs)
kept> build (lib + exe)
Preprocessing library for kept-0.1.0.0..
Building library for kept-0.1.0.0..

/mnt/backup/Programming/kept/src/Parse.hs:3:18: error:
    Found ‘qualified’ in postpositive position. 
    To allow this, enable language extension 'ImportQualifiedPost'
  |
3 | import Data.Text qualified as T
  |                  ^^^^^^^^^

--  While building package kept-0.1.0.0 (scroll up to its section to see the error) using:
      /home/ktf/.stack/setup-exe-cache/x86_64-linux-tinfo6/Cabal-simple_mPHDZzAJ_3.6.3.0_ghc-9.2.5 --verbose=1 --builddir=.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.6.3.0 build lib:kept exe:kept --ghc-options " -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1

And add to this the fact that standalone ormolu does not make this formatting change. Very curious that the behavior would differ.

Happy to close this if it's not an HLS bug after all. Just documenting my findings here in case it is, and/or in case it helps others who come across this strange behavior.

keithfancher avatar Jan 05 '23 06:01 keithfancher

Addendum:

After adding GHC2021 explicitly, the project builds. Also, with it explicitly enabled, standalone ormolu does move the qualified keyword in the same way as the HLS version.

Looks like there's some discrepancies -- not sure whether with HLS, ormolu, stack, or all of the above -- with the way the GHC2021 status is inferred.

I'm leaving it explicitly enabled, which seems to be the best choice anyway to avoid confusion.

I'll leave this issue open for now in case HLS is one of the offenders here, or any changes need to be made. But TBH, based on the GHC docs I linked above, HLS seems to be the only one doing the expected thing :smile:

keithfancher avatar Jan 05 '23 06:01 keithfancher

Also came across https://github.com/haskell/haskell-language-server/issues/2927, which could be related.

keithfancher avatar Jan 05 '23 06:01 keithfancher

I've been using Fourmolu and Ormolu (mostly the former, but the plugins are implemented very similarly) on GHC 9.2 for over half a year, and I've found that GHC2021 has been inferred when, and only when, I'd expect, including the default-language field being respected. Ormolu, on the command line, also correctly reads from default-language and default-extensions.

I don't use Stack though.

georgefst avatar Jan 17 '23 19:01 georgefst

Actually, reading again, could you clarify what the current issue is? It sounds like HLS is doing nothing wrong, and the issue may be that Stack or Ormolu incorrectly fails to infer GHC2021? Do you have a default-language field?

georgefst avatar Jan 17 '23 19:01 georgefst

Actually, reading again, could you clarify what the current issue is?

Sure thing, apologies. Now that I've poked around a bit more I understand the issue better and can summarize:

  • My default-language was set to Haskell2010. I did not have the ImportQualifiedPost extension enabled, just to be clear.
  • When I formatted a source file (that contained qualified imports) using ormolu (via HLS), the qualified keywords were moved to the end, as if I were using ImportQualifiedPost or GHC2021.
  • With these changes -- i.e. after using the HLS to format a file -- my project would not build. (Which makes perfect sense, since I wasn't using ImportQualifiedPost or GHC2021. But just noting as a sanity check.)
  • When I formatted that same source file with standalone ormolu, from the command-line, it didn't move my imports. (i.e. the behavior I would expect.) The issue only seems to occur with the version of ormolu that HLS is using.

It sounds like HLS is doing nothing wrong, and the issue may be that Stack or Ormolu incorrectly fails to infer GHC2021?

I'm honestly not sure where this bug lies. Is there a way to check which version of ormolu HLS is using? I couldn't find it, if so. I'm only seeing the bug when using HLS, but it could still be an ormolu issue.

I don't think there's a Stack issue, though -- that was my own misreading of the GHC docs. Stack is doing the right thing, as far as I can tell.

keithfancher avatar Jan 19 '23 06:01 keithfancher

By the way, a quick way to reproduce this is to just create a new stack project and add a qualified import:

$ stack new testing
$ cd testing
$ stack build  # succeeds

Then open up, e.g., app/Main.hs, and change import Lib to import qualified Lib and format the file using the HLS. This moves qualified to the end. The build will now fail:

    Found ‘qualified’ in postpositive position. 
    To allow this, enable language extension 'ImportQualifiedPost'

Note that the generated .cabal file explicitly sets Haskell2010 (and ImportQualifiedPost isn't enabled anywhere):

  default-language: Haskell2010

Here's everything I've got installed, all via ghcup:

✔✔ ghc   9.2.5    recommended,base-4.16.4.0 hls-powered
✔✔ cabal 3.6.2.0  recommended
✔✔ hls   1.9.0.0  latest,recommended
✔✔ stack 2.9.1    recommended
✔✔ ghcup 0.1.19.0 latest,recommended

(Though of course the version of GHC that stack is using is the one from the LTS. Still 9.2.5 though.)

keithfancher avatar Jan 19 '23 07:01 keithfancher

Thanks, can reproduce with Stack, and interestingly not with a similar Cabal skeleton project.

georgefst avatar Jan 21 '23 14:01 georgefst

hello, has this been fixed or is there a recommended way to handle this? Thanks

algo-1 avatar Dec 20 '23 14:12 algo-1