dense-analysis / ale

Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
BSD 2-Clause "Simplified" License
13.56k stars 1.44k forks source link

Stack-ghc linter produces errors when it shouldn't #1791

Closed JonnyRa closed 5 years ago

JonnyRa commented 6 years ago

Information

VIM version VIM - Vi IMproved 8.1 (2018 May 18, compiled Jun 26 2018 09:35:41)
Included patches: 1-116

Operating System: Ubuntu 18.04

:ALEInfo

 Current Filetype: haskell
Available Linters: ['ghc-mod', 'stack-ghc-mod', 'ghc', 'hdevtools', 'hlint', 'stack-build', 'stack-ghc']
  Enabled Linters: ['hdevtools', 'hlint', 'stack-ghc']
 Suggested Fixers: 
  'brittany' - Fix Haskell files with brittany.
  'hfmt' - Fix Haskell files with hfmt.
  'remove_trailing_lines' - Remove all blank lines at the end of a file.
  'trim_whitespace' - Remove all trailing whitespace characters at the end of every line.
 Linter Variables:

let g:ale_haskell_hdevtools_executable = 'hdevtools'
let g:ale_haskell_hdevtools_options = '-g -Wall'
 Global Variables:

let g:ale_cache_executable_check_failures = 0
let g:ale_change_sign_column_color = 0
let g:ale_command_wrapper = ''
let g:ale_completion_delay = 100
let g:ale_completion_enabled = 0
let g:ale_completion_max_suggestions = 50
let g:ale_echo_cursor = 1
let g:ale_echo_msg_error_str = 'Error'
let g:ale_echo_msg_format = '%code: %%s'
let g:ale_echo_msg_info_str = 'Info'
let g:ale_echo_msg_warning_str = 'Warning'
let g:ale_enabled = 1
let g:ale_fix_on_save = 0
let g:ale_fixers = {}
let g:ale_history_enabled = 1
let g:ale_history_log_output = 1
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = 1
let g:ale_lint_on_filetype_changed = 1
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 'always'
let g:ale_lint_on_insert_leave = 0
let g:ale_linter_aliases = {}
let g:ale_linters = {'vim': ['vint'], 'cs': [], 'haskell': ['stack-ghc', 'hlint', 'hdevtools', 'hfmt']}
let g:ale_linters_explicit = 0
let g:ale_list_window_size = 10
let g:ale_list_vertical = 0
let g:ale_loclist_msg_format = '%code: %%s'
let g:ale_max_buffer_history_size = 20
let g:ale_max_signs = -1
let g:ale_maximum_file_size = 0
let g:ale_open_list = 0
let g:ale_pattern_options = {}
let g:ale_pattern_options_enabled = 0
let g:ale_set_balloons = 0
let g:ale_set_highlights = 1
let g:ale_set_loclist = 1
let g:ale_set_quickfix = 0
let g:ale_set_signs = 1
let g:ale_sign_column_always = 0
let g:ale_sign_error = '>>'
let g:ale_sign_info = '--'
let g:ale_sign_offset = 1000000
let g:ale_sign_style_error = '>>'
let g:ale_sign_style_warning = '--'
let g:ale_sign_warning = '--'
let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK']
let g:ale_type_map = {}
let g:ale_use_global_executables = v:null
let g:ale_warn_about_trailing_blank_lines = 1
let g:ale_warn_about_trailing_whitespace = 1
  Command History:

(finished - exit code 1) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/710/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[{"module":"","decl":"","severity":"Error","hint":"Parse error","file":"-","startLine":3,"startColumn":13,"endLine":3,"endColumn":13,"from":"  {-# LANGUAGE OverloadedStrings #-}\n  \n> module Types. (spec) where\n  \n  import           Data.Maybe                            (fromJust)\n","to":null,"note":[],"refactorings":"[]"}]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/711/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/711/ImportSpec.hs:3:13: error:
    parse error on input ‘.’
  |
3 | module Types. (spec) where
  |             ^
<<<OUTPUT ENDS>>>

(started) ['/bin/bash', '-c', '''hdevtools'' check -g -Wall -p ''/home/jonathanramsden/repos/tracs-enterprise/trent-testing/test/Types/Control/ImportSpec.hs'' ''/tmp/vgbPARv/712/ImportSpec.hs''']
(finished - exit code 0) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/713/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/714/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/714/ImportSpec.hs:11:1: error:
    Could not find module ‘Test.DaysOfOperation’
    Use -v to see a list of the files searched for.
   |
11 | import Test.DaysOfOperation
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
<<<OUTPUT ENDS>>>

(started) ['/bin/bash', '-c', '''hdevtools'' check -g -Wall -p ''/home/jonathanramsden/repos/tracs-enterprise/trent-testing/test/Types/Control/ImportSpec.hs'' ''/tmp/vgbPARv/715/ImportSpec.hs''']
(finished - exit code 1) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/716/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[{"module":"","decl":"","severity":"Error","hint":"Parse error","file":"-","startLine":3,"startColumn":21,"endLine":3,"endColumn":21,"from":"  {-# LANGUAGE OverloadedStrings #-}\n  \n> module Types.Control. (spec) where\n  \n  import           Data.Maybe                            (fromJust)\n","to":null,"note":[],"refactorings":"[]"}]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/717/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/717/ImportSpec.hs:3:21: error:
    parse error on input ‘.’
  |
3 | module Types.Control. (spec) where
  |                     ^
<<<OUTPUT ENDS>>>

(started) ['/bin/bash', '-c', '''hdevtools'' check -g -Wall -p ''/home/jonathanramsden/repos/tracs-enterprise/trent-testing/test/Types/Control/ImportSpec.hs'' ''/tmp/vgbPARv/718/ImportSpec.hs''']
(finished - exit code 0) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/719/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/720/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/720/ImportSpec.hs:11:1: error:
    Could not find module ‘Test.DaysOfOperation’
    Use -v to see a list of the files searched for.
   |
11 | import Test.DaysOfOperation
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
<<<OUTPUT ENDS>>>

(started) ['/bin/bash', '-c', '''hdevtools'' check -g -Wall -p ''/home/jonathanramsden/repos/tracs-enterprise/trent-testing/test/Types/Control/ImportSpec.hs'' ''/tmp/vgbPARv/721/ImportSpec.hs''']
(finished - exit code 0) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/722/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/723/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/723/ImportSpec.hs:11:1: error:
    Could not find module ‘Test.DaysOfOperation’
    Use -v to see a list of the files searched for.
   |
11 | import Test.DaysOfOperation
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', '''hdevtools'' check -g -Wall -p ''/home/jonathanramsden/repos/tracs-enterprise/trent-testing/test/Types/Control/ImportSpec.hs'' ''/tmp/vgbPARv/724/ImportSpec.hs''']

<<<NO OUTPUT RETURNED>>>

(finished - exit code 0) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/725/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/726/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/726/ImportSpec.hs:11:1: error:
    Could not find module ‘Test.DaysOfOperation’
    Use -v to see a list of the files searched for.
   |
11 | import Test.DaysOfOperation
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', '''hdevtools'' check -g -Wall -p ''/home/jonathanramsden/repos/tracs-enterprise/trent-testing/test/Types/Control/ImportSpec.hs'' ''/tmp/vgbPARv/727/ImportSpec.hs''']

<<<NO OUTPUT RETURNED>>>

(finished - exit code 0) ['/bin/bash', '-c', 'hlint --color=never --json - < ''/tmp/vgbPARv/728/ImportSpec.hs''']

<<<OUTPUT STARTS>>>
[]
<<<OUTPUT ENDS>>>

(finished - exit code 1) ['/bin/bash', '-c', 'stack ghc -- -fno-code -v0 ''/tmp/vgbPARv/729/ImportSpec.hs''']

<<<OUTPUT STARTS>>>

/tmp/vgbPARv/729/ImportSpec.hs:11:1: error:
    Could not find module ‘Test.DaysOfOperation’
    Use -v to see a list of the files searched for.
   |
11 | import Test.DaysOfOperation
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
<<<OUTPUT ENDS>>>

What went wrong

That (repeated) import error is incorrect - the code compiles fine. You can safely ignore the hdevtools related stuff.

The stack-ghc linter is the most useful one but it sometimes produces errors where there should be none. They typically seem to relate to files inside the same package as the target file - eg other files outside the package will be fine.

The bug is intermittent - I believe it only happens when temporary files are used and I think it could possibly be fixed by supplying stack with a working directory, however I've done a bit of digging and am struggling to reproduce the same output when calling stack-ghc on a copy of the file directly.

Could you help me understand what ALE does? I've been reading the source a bit but been finding it a bit hard to follow and haven't seen anything about working directories.

I saved a copy of the file in question to the same tmp path in the hope of recreating the issue.

I've been running stack ghc like this bash -c 'stack ghc -- -fno-code -v0 /tmp/vgbPARv/729/ImportSpec.hs' which is more or less what I copied from the log above.

The two ways I've tried running stack-ghc on the same file are:

Since I can't reproduce the output with stack ghc I came to the conclusion that ale must be running stack differently than either of the above.

Could you help shed some light?

If I can reproduce the errors/environment I should be able to help suggest how to run stack ghc to fix this issue/maybe even go in and update the linter myself and submit a pull request.

Btw I love ALE, it's a massive boost to the usefulness of vim when writing Haskell even with this niggle. Nice one for writing such a useful plugin :)

Cheers

Jonny

w0rp commented 6 years ago

I'll say that's a bug and it can be fixed by changing the working directory. That could be done with a command_callback function and ale#path#BufferCdString. Something like this:

'command_callback': {buffer -> ale#path#BufferCdString(buffer) . 'stack ghc -- -fno-code -v0 %t'},
JonnyRa commented 6 years ago

Thanks for the response

I'll give that a try in my local copy and see if it fixes the issue

does it use the vim working directory by default? I'm pretty sure that'd work fine aswell.

Cheers

Jonny

w0rp commented 6 years ago

That sets the working directory to be the directory that the file is in all the time, with cd ... &&

JonnyRa commented 6 years ago

does that include when it gets copied to tmp?

I think it'd actually be better to either set it to the vim working directory or possibly to the buffer's original location. Does that call do the latter? I'm not sure which is the right answer as I can't reproduce those specific errors by calling stack ghc from the command line

Is the default behaviour without the extra command to use :pwd as the working directory?

w0rp commented 6 years ago

It sets it to the directory the file is in, which should be inside the project. Give it a try and see how well it works. It tends to work for most tools.

The default is that the working directory is effectively random, as it's :pwd at the time the command runs, which could be at any time.

mvaldesdeleon commented 5 years ago

Hi @w0rp . First of all, thanks for a great plugin.

I had a similar issue as OP and was able to work around it by following your suggestion to change the working directory to this file: https://github.com/w0rp/ale/blob/master/ale_linters/haskell/stack_ghc.vim

I can make a PR for this but I'm not sure if it should be changed for everybody, or if it would make more sense to make this behaviour configurable.

What's the usual approach?

w0rp commented 5 years ago

The usual approach is to use the ale#path#BufferCdString(a:buffer) function to set the working directory to the directory the file is in, or similar. See other linters for examples. Feel free to create a pull request.

drewboardman commented 5 years ago

I'm still having this issue.

There seems to be an issue with the stack_ghc linter that isn't present in the ghcid running inside vim (via the plugin). The linter claims that local modules that are imported cannot be resolved.

.vimrc setup

let g:ale_linters = {}
let g:ale_linters.haskell = ['stack_ghc', 'hlint']
let g:ale_haskell_stack_ghc_options = '
      \ -threaded
      \ -rtsopts
      \ -with-rtsopts=-N
      \ -Wall
      \ -Wcompat
      \ -Wincomplete-record-updates
      \ -Wincomplete-uni-patterns
      \ -Wredundant-constraints
      \ -Wmissing-import-lists
      \ -Wmissing-export-lists
      \'

let g:ghcid_command = 'stack exec ghcid --'

In the following file, I'm importing the Models module.

# DatabaseThings.hs

     9 -- import           Database.Beam.Sqlite                                                                                                                                                
    10 import qualified Data.Time.Clock as Time (UTCTime)                                                                                                                                      
    11 import           Database.Beam   (Columnar, Generic, Identity, PrimaryKey)                                                                                                              
✘   12 import           Models          (Content) 

#### this error shows
Could not find module ‘Models’ Perhaps you meant Module (needs flag -package-key ghc-8.6... import           Models          (Content) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Running ghcid in either vim or the terminal shows no issue.

All good (2 modules, at 18:47:24)

Here is the directory structure

├── src
│   ├── DatabaseThings.hs
│   └── Models.hs

It seems as if the linter is potentially not running in the pwd of the project, but that's just my guess.

JonnyRa commented 5 years ago

This also isn't resolved for me, I never really got my head around what is happening and I couldn't reproduce the error just using stack and running with different working directories.

I don't think the suggested patch helped but am unsure whether I am running a locally patched version (which still has the problem) or whether I updated ALE at some point

drewboardman commented 5 years ago

I'd like to try and fix this, but I'm pretty unfamiliar with the syntax used in these files. Can you give me some pointers on how to make this compile?

" Author: w0rp <devw0rp@gmail.com>
" Description: ghc for Haskell files, using Stack

call ale#Set('haskell_stack_ghc_options', '-fno-code -v0')

function! ale_linters#haskell#stack_ghc#GetCommand(buffer) abort
    return ale#handlers#haskell#GetStackExecutable(a:buffer)
    \ . ' ghc -- '
    \ . ale#Var(a:buffer, 'haskell_stack_ghc_options')
    \ . ' %t'
endfunction

call ale#linter#Define('haskell', {
\   'name': 'stack_ghc',
\   'aliases': ['stack-ghc'],
\   'output_stream': 'stderr',
\   'executable': function('ale#handlers#haskell#GetStackExecutable'),
\   'command': function('ale_linters#haskell#stack_ghc#GetCommand'),
\   'callback': 'ale#handlers#haskell#HandleGHCFormat',
\   'command_callback': {buffer -> ale#path#BufferCdString(buffer) . 'stack ghc -- -fno-code -v0 %t'}
\})

That Map takes strings and functions it seems.