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

Hlint suggestion produces invalid code (breaks block identation) (revisit after mpickering/apply-refact#95) #593

Closed
emlautarom1 opened this issue Nov 12, 2020 · 3 comments
Labels
component: hls-hlint-plugin status: blocked Not actionable, because blocked by upstream/GHC etc. type: bug Something isn't right: doesn't work as intended, documentation is missing/outdated, etc..

Comments

@emlautarom1
Copy link

Your environment

Output of haskell-language-server --probe-tools or haskell-language-server-wrapper --probe-tools:

haskell-language-server version: 0.6.0.0 (GHC: 8.8.4) (PATH: /home/emlautarom1/.config/Code/User/globalStorage/haskell.haskell/haskell-language-server-0.6.0-linux-8.8.4) (GIT hash: 372a12e797069dc3ac4fa33dcaabe3b992999d7c)
Tool versions found on the $PATH
cabal:		3.2.0.0
stack:		Not found
ghc:		8.8.4

Which lsp-client do you use: VSCode
Describe your project (alternative: link to the project): Single .hs file
Contents of hie.yaml: I'm using implicit cradle (no hie.yaml)

Steps to reproduce

  1. Define a function like the following:
-- The body of f was generated using tactics (case split, introduce lambda, etc)
f :: Bool -> Integer
f = (\ b ->  (case b of
   False -> 1
   True -> 0))
  1. Apply the refactors suggested by hlint. The resulting code is:
f :: Bool -> Integer
f b = case b of
False -> 1
True -> 0

Expected behaviour

Applying hints should produce working code. The CLI of hlint produces the correct output (it compiles):

Factorial.hs:(2,1)-(4,14): Warning: Redundant lambda
Found:
  f = (\ b
         -> (case b of
               False -> 1
               True -> 0))
Perhaps:
  f b
    = case b of
        False -> 1
        True -> 0

Factorial.hs:(2,5)-(4,14): Suggestion: Redundant bracket
Found:
  (\ b
     -> (case b of
           False -> 1
           True -> 0))
Perhaps:
  \ b
    -> (case b of
          False -> 1
          True -> 0)

Factorial.hs:(2,15)-(4,12): Suggestion: Use if
Found:
  case b of
    False -> 1
    True -> 0
Perhaps:
  if b then 0 else 1

3 hints

Actual behaviour

The result of applying the suggestion produces code that does not compile anymore since there's an error on line 3: parse error on input ‘->’, since the False and True branches are missing whitespace at the beginning of the line.

Include debug information

Execute in the root of your project the command haskell-language-server --debug . and paste the logs here:

Debug output:
(haskell-language-server)Ghcide setup tester in /home/emlautarom1/.config/Code/User/globalStorage/haskell.haskell.
Report bugs at https://github.com/haskell/haskell-language-server/issues

Tool versions found on the $PATH
cabal:		3.2.0.0
stack:		Not found
ghc:		8.8.4


Step 1/4: Finding files to test in /home/emlautarom1/.config/Code/User/globalStorage/haskell.haskell
Found 1 files

Step 2/4: Looking for hie.yaml files that control setup
Found 1 cradle

Step 3/4: Initializing the IDE

Step 4/4: Type checking the files
[INFO] Consulting the cradle for "/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"
NotShowMessage (NotificationMessage {_jsonrpc = "2.0", _method = WindowShowMessage, _params = ShowMessageParams {_xtype = MtInfo, _message = "No [cradle](https://github.com/mpickering/hie-bios#hie-bios) found for /home/emlautarom1/Development/Haskell/Snippets/Factorial.hs.\n Proceeding with [implicit cradle](https://hackage.haskell.org/package/implicit-hie).\nYou should ignore this message, unless you see a 'Multi Cradle: No prefixes matched' error."}})
[INFO] Using interface files cache dir: /home/emlautarom1/.cache/ghcide/main-da39a3ee5e6b4b0d3255bfef95601890afd80709
[INFO] Making new HscEnv[main]
[INFO] finish: User TypeCheck (took 0.02s)

Completed (1 file worked, 0 files failed)

Paste the logs from the lsp-client, e.g. for VS Code

LSP logs:
2020-11-12 12:07:42.210615514 [ThreadId 1319] - DocumentHighlight request at position 2:8 in file: /home/emlautarom1/Development/Haskell/Snippets/Factorial.hs
2020-11-12 12:07:42.460261317 [ThreadId 1326] - finish: CodeAction (took 0.00s)
2020-11-12 12:07:42.460435735 [ThreadId 1331] - finish: CodeAction:PackageExports (took 0.00s)
2020-11-12 12:07:42.460818634 [ThreadId 1332] - finish: importLens (took 0.00s)
2020-11-12 12:07:42.46101887 [ThreadId 1334] - finish: retrie (took 0.00s)
2020-11-12 12:07:42.461206202 [ThreadId 1336] - finish: tactic (took 0.00s)
2020-11-12 12:07:42.461402361 [ThreadId 1341] - finish: tactic (took 0.00s)
2020-11-12 12:07:42.461528588 [ThreadId 1342] - finish: tactic (took 0.00s)
2020-11-12 12:07:43.603960105 [ThreadId 1348] - finish: CodeAction (took 0.00s)
2020-11-12 12:07:43.6040479 [ThreadId 1350] - finish: CodeAction:PackageExports (took 0.00s)
2020-11-12 12:07:43.604295896 [ThreadId 1352] - finish: importLens (took 0.00s)
2020-11-12 12:07:43.604504568 [ThreadId 1354] - finish: retrie (took 0.00s)
2020-11-12 12:07:43.604726344 [ThreadId 1356] - finish: tactic (took 0.00s)
2020-11-12 12:07:43.60479846 [ThreadId 1358] - finish: tactic (took 0.00s)
2020-11-12 12:07:43.604908927 [ThreadId 1360] - finish: tactic (took 0.00s)
2020-11-12 12:07:44.755344189 [ThreadId 1366] - hlint:applyAllCmd:file=NormalizedFilePath "/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"
2020-11-12 12:07:44.755538103 [ThreadId 1367] - hlint:getIdeas:file:NormalizedFilePath "/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"
2020-11-12 12:07:44.755655884 [ThreadId 1367] - hlint:getIdeas:setExtensions:[Cpp,OverlappingInstances,UndecidableInstances,IncoherentInstances,UndecidableSuperClasses,MonomorphismRestriction,MonoPatBinds,MonoLocalBinds,RelaxedPolyRec,ExtendedDefaultRules,ForeignFunctionInterface,UnliftedFFITypes,InterruptibleFFI,CApiFFI,GHCForeignImportPrim,JavaScriptFFI,ParallelArrays,TemplateHaskell,TemplateHaskellQuotes,ImplicitParams,ImplicitPrelude,ScopedTypeVariables,AllowAmbiguousTypes,UnliftedNewtypes,BangPatterns,TypeFamilies,TypeFamilyDependencies,TypeInType,OverloadedStrings,OverloadedLists,NumDecimals,DisambiguateRecordFields,RecordWildCards,RecordPuns,ViewPatterns,GADTs,GADTSyntax,NPlusKPatterns,DoAndIfThenElse,BlockArguments,RebindableSyntax,ConstraintKinds,PolyKinds,DataKinds,InstanceSigs,ApplicativeDo,StandaloneDeriving,DeriveDataTypeable,AutoDeriveTypeable,DeriveFunctor,DeriveTraversable,DeriveFoldable,DeriveGeneric,DefaultSignatures,DeriveAnyClass,DeriveLift,DerivingStrategies,DerivingVia,TypeSynonymInstances,FlexibleContexts,FlexibleInstances,ConstrainedClassMethods,MultiParamTypeClasses,NullaryTypeClasses,FunctionalDependencies,UnicodeSyntax,ExistentialQuantification,MagicHash,EmptyDataDecls,KindSignatures,RoleAnnotations,ParallelListComp,MonadComprehensions,GeneralizedNewtypeDeriving,PostfixOperators,TupleSections,PatternGuards,LiberalTypeSynonyms,RankNTypes,ImpredicativeTypes,TypeOperators,ExplicitNamespaces,PackageImports,ExplicitForAll,AlternativeLayoutRuleTransitional,DatatypeContexts,NondecreasingIndentation,RelaxedLayout,TraditionalRecordSyntax,LambdaCase,MultiWayIf,BinaryLiterals,HexFloatLiterals,DuplicateRecordFields,OverloadedLabels,EmptyCase,PatternSynonyms,PartialTypeSignatures,NamedWildCards,TypeApplications,Strict,StrictData,MonadFailDesugaring,EmptyDataDeriving,NumericUnderscores,QuantifiedConstraints,ImportQualifiedPost,CUSKs,StandaloneKindSignatures,StarIsType]
2020-11-12 12:07:44.756260841 [ThreadId 1367] - finish: applyHint (took 0.00s)
2020-11-12 12:07:44.756262053 [ThreadId 1366] - applyHint:apply=[("/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs:(2,1)-(4,14): Warning: Redundant lambda\nFound:\n  f = (\\ b\n         -> (case b of\n               False -> 1\n               True -> 0))\nPerhaps:\n  f b\n    = case b of\n        False -> 1\n        True -> 0\n",[Replace {rtype = Decl, pos = SrcSpan {startLine = 2, startCol = 1, endLine = 4, endCol = 15}, subts = [("body",SrcSpan {startLine = 2, startCol = 15, endLine = 4, endCol = 13}),("a",SrcSpan {startLine = 2, startCol = 8, endLine = 2, endCol = 9})], orig = "f a = body"}]),("/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs:(2,5)-(4,14): Suggestion: Redundant bracket\nFound:\n  (\\ b\n     -> (case b of\n           False -> 1\n           True -> 0))\nPerhaps:\n  \\ b\n    -> (case b of\n          False -> 1\n          True -> 0)\n",[Replace {rtype = Expr, pos = SrcSpan {startLine = 2, startCol = 5, endLine = 4, endCol = 15}, subts = [("x",SrcSpan {startLine = 2, startCol = 6, endLine = 4, endCol = 14})], orig = "x"}]),("/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs:(2,15)-(4,12): Suggestion: Use if\nFound:\n  case b of\n    False -> 1\n    True -> 0\nPerhaps:\n  if b then 0 else 1\n",[Replace {rtype = Expr, pos = SrcSpan {startLine = 2, startCol = 15, endLine = 4, endCol = 13}, subts = [("a",SrcSpan {startLine = 2, startCol = 20, endLine = 2, endCol = 21}),("f",SrcSpan {startLine = 3, startCol = 13, endLine = 3, endCol = 14}),("t",SrcSpan {startLine = 4, startCol = 12, endLine = 4, endCol = 13})], orig = "if a then t else f"}])]
2020-11-12 12:07:44.759357908 [ThreadId 1369] - finish: hlint (took 0.00s)
2020-11-12 12:07:44.763807536 [ThreadId 1366] - hlint:applyHint:diff=WorkspaceEdit {_changes = Nothing, _documentChanges = Just (List [TextDocumentEdit {_textDocument = VersionedTextDocumentIdentifier {_uri = Uri {getUri = "file:///home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"}, _version = Just 0}, _edits = List [TextEdit {_range = Range {_start = Position {_line = 1, _character = 0}, _end = Position {_line = 3, _character = 14}}, _newText = "f b = case b of\nFalse -> 1\nTrue -> 0"}]}])}
2020-11-12 12:07:44.769548088 [ThreadId 1366] - hlint:applyAllCmd:res=Right (WorkspaceEdit {_changes = Nothing, _documentChanges = Just (List [TextDocumentEdit {_textDocument = VersionedTextDocumentIdentifier {_uri = Uri {getUri = "file:///home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"}, _version = Just 0}, _edits = List [TextEdit {_range = Range {_start = Position {_line = 1, _character = 0}, _end = Position {_line = 3, _character = 14}}, _newText = "f b = case b of\nFalse -> 1\nTrue -> 0"}]}])})
2020-11-12 12:07:44.780002585 [ThreadId 1375] - finish: CodeAction (took 0.00s)
2020-11-12 12:07:44.780112922 [ThreadId 1377] - finish: CodeAction:PackageExports (took 0.00s)
2020-11-12 12:07:44.780488317 [ThreadId 1379] - finish: importLens (took 0.00s)
2020-11-12 12:07:44.780748887 [ThreadId 1381] - finish: retrie (took 0.00s)
2020-11-12 12:07:44.780965634 [ThreadId 1383] - finish: tactic (took 0.00s)
2020-11-12 12:07:44.781040625 [ThreadId 1385] - finish: tactic (took 0.00s)
2020-11-12 12:07:44.781099586 [ThreadId 1387] - finish: tactic (took 0.00s)
2020-11-12 12:07:44.790892931 [ThreadId 27] - Modified text document: file:///home/emlautarom1/Development/Haskell/Snippets/Factorial.hs
2020-11-12 12:07:44.792439295 [ThreadId 1446] - getClientConfigAction:clientSettings:Just (Object (fromList [("haskell",Object (fromList [("logFile",String ""),("updateBehavior",String "keep-up-to-date"),("hlintOn",Bool True),("formatOnImportOn",Bool True),("indentationRules",Object (fromList [("enabled",Bool True)])),("liquidOn",Bool False),("languageServerVariant",String "haskell-language-server"),("serverExecutablePath",String ""),("diagnosticsOnChange",Bool True),("completionSnippetsOn",Bool True),("maxNumberOfProblems",Number 100.0),("formattingProvider",String "ormolu"),("trace",Object (fromList [("server",String "off")]))]))]))
2020-11-12 12:07:44.792555503 [ThreadId 1446] - hlint:getIdeas:file:NormalizedFilePath "/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"
2020-11-12 12:07:45.033214908 [ThreadId 1457] - Plugin.makeCodeLens (ideLogger)
2020-11-12 12:07:45.033668811 [ThreadId 1458] - finish: codeLens (took 0.00s)
2020-11-12 12:07:45.034047933 [ThreadId 1463] - finish:  (took 0.00s)
2020-11-12 12:07:45.034229744 [ThreadId 1464] - finish: ModuleName.ghcSession (took 0.00s)
2020-11-12 12:07:45.034591714 [ThreadId 1466] - finish: ModuleName.GetParsedModule (took 0.00s)
2020-11-12 12:07:45.035223381 [ThreadId 1471] - Plugin.makeCodeLens (ideLogger)
2020-11-12 12:07:45.035411003 [ThreadId 1472] - finish: codeLens (took 0.00s)
2020-11-12 12:07:45.035641205 [ThreadId 1474] - finish:  (took 0.00s)
2020-11-12 12:07:45.035762173 [ThreadId 1476] - finish: ModuleName.ghcSession (took 0.00s)
2020-11-12 12:07:45.035955716 [ThreadId 1478] - finish: ModuleName.GetParsedModule (took 0.00s)
2020-11-12 12:07:45.048052247 [ThreadId 1484] - finish: CodeAction (took 0.00s)
2020-11-12 12:07:45.048233367 [ThreadId 1489] - finish: CodeAction:PackageExports (took 0.00s)
2020-11-12 12:07:45.048382488 [ThreadId 1490] - finish: importLens (took 0.00s)
2020-11-12 12:07:45.048519495 [ThreadId 1492] - finish: retrie (took 0.00s)
2020-11-12 12:07:45.048706145 [ThreadId 1494] - finish: tactic (took 0.00s)
2020-11-12 12:07:45.048875544 [ThreadId 1499] - finish: tactic (took 0.00s)
2020-11-12 12:07:45.048915799 [ThreadId 1500] - finish: tactic (took 0.00s)
2020-11-12 12:07:45.291323207 [ThreadId 27] - Saved text document: file:///home/emlautarom1/Development/Haskell/Snippets/Factorial.hs
2020-11-12 12:07:45.29272473 [ThreadId 1562] - Typechecking reverse dependencies forNormalizedFilePath "/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs": []
2020-11-12 12:07:45.292827553 [ThreadId 1566] - getClientConfigAction:clientSettings:Just (Object (fromList [("haskell",Object (fromList [("logFile",String ""),("updateBehavior",String "keep-up-to-date"),("hlintOn",Bool True),("formatOnImportOn",Bool True),("indentationRules",Object (fromList [("enabled",Bool True)])),("liquidOn",Bool False),("languageServerVariant",String "haskell-language-server"),("serverExecutablePath",String ""),("diagnosticsOnChange",Bool True),("completionSnippetsOn",Bool True),("maxNumberOfProblems",Number 100.0),("formattingProvider",String "ormolu"),("trace",Object (fromList [("server",String "off")]))]))]))
2020-11-12 12:07:45.292990799 [ThreadId 1566] - hlint:getIdeas:file:NormalizedFilePath "/home/emlautarom1/Development/Haskell/Snippets/Factorial.hs"

@jneira jneira added component: hls-hlint-plugin type: bug Something isn't right: doesn't work as intended, documentation is missing/outdated, etc.. labels Nov 12, 2020
@jneira
Copy link
Member

jneira commented Nov 13, 2020

Hi @emlautarom1, thanks for the bug report. The error is reproduced by the plugin but too by the cli (hlint + apply-refact) in my tests:

PS D:\dev\ws\haskell\cabal-test> cat .\src\HlintTests.hs
module HlintTests where

g :: Bool -> Integer
g = (\ b ->  (case b of
   False -> 1
   True -> 0))

PS D:\dev\ws\haskell\cabal-test> hlint .\src\HlintTests.hs --refactor
module HlintTests where

g :: Bool -> Integer
g b = case b of
False -> 1
True -> 0

PS D:\dev\ws\haskell\cabal-test> hlint --version
HLint v3.2.1, (C) Neil Mitchell 2006-2020
PS D:\dev\ws\haskell\cabal-test> refactor --version
v0.8.2.1

So i am afraid that should be fixed upstream. @soiamsoNG has created already a test case: https://github.com/mpickering/apply-refact/pull/92/files

@jneira jneira added the status: blocked Not actionable, because blocked by upstream/GHC etc. label Nov 13, 2020
@jneira jneira changed the title Hlint suggestion produces invalid code Hlint suggestion produces invalid code (breaks block identation) Nov 17, 2020
@jneira
Copy link
Member

jneira commented Nov 28, 2020

We reported the issue upstream and the temporary workaround will be not apply the refactoring if it would break identation until ghc-exactprint is fixed

@Anton-Latukha Anton-Latukha changed the title Hlint suggestion produces invalid code (breaks block identation) Hlint suggestion produces invalid code (breaks block identation) (revisit after mpickering/apply-refact#95) Dec 25, 2021
@michaelpj
Copy link
Collaborator

Closing as it's just an upstream issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: hls-hlint-plugin status: blocked Not actionable, because blocked by upstream/GHC etc. type: bug Something isn't right: doesn't work as intended, documentation is missing/outdated, etc..
Projects
None yet
Development

No branches or pull requests

3 participants