phoityne / haskell-debug-adapter

Debug Adapter for Haskell debugging system.
https://hackage.haskell.org/package/haskell-debug-adapter
BSD 3-Clause "New" or "Revised" License
53 stars 8 forks source link

Step Into Sometimes Off by One Line #13

Open lordmilko opened 4 years ago

lordmilko commented 4 years ago

Sometimes when debugging in Visual Studio or Visual Studio Code, stepping into a function on the line before the function is actually called will cause two lines of code to be executed

For example,

Consider the following Main.hs

module Main where

main2 = do
  putStrLn "a"
  putStrLn "b"

main :: IO ()
main = do
  putStrLn "hello world"
  putStrLn "c"
  main2

If you put a breakpoint on line 9 in Visual Studio (putStrLn "hello world") and then run the debugger and then hit F11 twice, you'll find that the following will occur

  1. Line 9 is executed and we step to line 10
  2. Line 10 and 11 are executed and we immediately step into the function main2

This is incorrect. Rather, we expect the following should occur instead

  1. Line 9 is executed and we step to line 10
  2. Line 10 is executed and we step to line 11

If you remove the declaration of main :: IO () however, you'll find that suddenly debugging works normal as expected. As such, it appears the main :: IO () line is throwing haskell-debug-engine off somehow

I am also able to replicate the same behavior in Visual Studio Code, except that in Code, removing main :: IO () does not fix the issue

If you modify the function main to take a single argument and pass it an integer in Visual Studio, the issue also goes away

Also worth noting when this happens in Visual Studio, you can see the the yellow text indicating the position of the code the debugger has broken on is all messed up image

phoityne commented 4 years ago

Basically, debugging is the functionality of ghci itself. hda just displays the ghci result on the ide. You can see the text output from ghci on the console window.

Could you check "step move" by debugging directly running ghci on the command prompt ? (would be same "step move" with ide, also your other issues, such as scope variables.)

Regards.

lordmilko commented 4 years ago

Hi @phoityne,

It appears the same issues happen when debugging with GHCi

In the following test, I perform the following

  1. Set a breakpoint on line 9
  2. Run main
  3. When I hit the breakpoint on line 9, I display my current position
  4. I single step within the main function, and confirm my current position is now the second line, putStrLn "c"
  5. I then single step again, except this time you can see GHCi completely steps over the final line and ends the program
*Main> :break 9
Breakpoint 0 activated at C:\debugExample\src\Main.hs:9:3-24
*Main> main
Stopped in Main.main, C:\debugExample\src\Main.hs:9:3-24
_result :: IO () = _
[C:\debugExample\src\Main.hs:9:3-24] *Main> :list
8  main = do
9    putStrLn "hello world"
     ^^^^^^^^^^^^^^^^^^^^^^
10    putStrLn "c"
[C:\debugExample\src\Main.hs:9:3-24] *Main> :steplocal
hello world
Stopped in Main.main, C:\debugExample\src\Main.hs:10:3-14
_result :: IO () = _
[C:\debugExample\src\Main.hs:10:3-14] *Main> :list
9    putStrLn "hello world"
10    putStrLn "c"
      ^^^^^^^^^^^^
11    main2
[C:\debugExample\src\Main.hs:10:3-14] *Main> :steplocal
c
a
b
*Main>

In the case of the weird formatting issue displayed in the screenshot, it appears this behavior actually may be the result of some tabs having been inserted, instead of spaces

Given the code

module Main where

main2 v = do
    putStrLn "aaaaaaaaaaaaaa"
    putStrLn $ show v

main :: IO ()
main = do
  putStrLn "hello world"
  putStrLn "c"
  main2 3

When I run stack ghci it first states

C:\debugExample\src\Main.hs:4:1: warning: [-Wtabs]
    Tab character found here, and in one further location.
    Please use spaces instead.
  |
4 |         putStrLn "aaaaaaaaaaaaaa"
  | ^^^^^^^^

When I eventually step into the function via the :stepmodule command, you can see that GHCI's analysis of where the text is is off here as well

Stopped in Main.main, C:\debugExample\src\Main.hs:11:3-9
_result :: IO () = _
[C:\debugExample\src\Main.hs:11:3-9] *Main> :list
10    putStrLn "c"
11    main2 3
      ^^^^^^^
[C:\debugExample\src\Main.hs:11:3-9] *Main> :stepmodule
Stopped in Main.main2, C:\debugExample\src\Main.hs:(3,11)-(5,25)
_result :: IO () = _
v :: Integer = 3
[C:\debugExample\src\Main.hs:(3,11)-(5,25)] *Main> :list
2
           vv
3  main2 v = do
4       putStrLn "aaaaaaaaaaaaaa"
5       putStrLn $ show v
                            ^^
6
[C:\debugExample\src\Main.hs:(3,11)-(5,25)] *Main> :steplocal
Stopped in Main.main2, C:\debugExample\src\Main.hs:4:9-33
_result :: IO () = _
[C:\debugExample\src\Main.hs:4:9-33] *Main> :list
3  main2 v = do
4       putStrLn "aaaaaaaaaaaaaa"
           ^^^^^^^^^^^^^^^^^^^^^^^^^
5       putStrLn $ show v
[C:\debugExample\src\Main.hs:4:9-33] *Main>

I am still investigating why the former issue with GHCi stepping over the last line occurs, however I think it would be worth documenting on this project all of the shortcomings of GHCi that users should be aware of when trying to utilize haskell-debug-adapter (such as the requirement for spaces over tabs, why it might skip over lines, variable scope, etc), so that users aren't surprised when debugging Haskell doesn't work like debugging other languages. Potentially either on the README itself, or a document/wiki article the README refers to.