Weird interaction between CPP #if blocks and haddock comments
In the code given below ok works as expected: when compiling with ghc >= 9 the example will be show a in the docs, otherwise show b.
Only for this to work correctly with doctest there needs to be a blank line between the :} and the error message.
So in broken I've added this <BLANKLINE> only in the first branch of the #if and when this branch is used (compiling with ghc-9.0.1) everything works fine.
But when when the #else branch is used (compiling with ghc < 9), the documentation for broken is rendered as:
broken # 31 "src/MyLib.hs" >>> :{ show b :} interactive:...: error: Variable not in scope: b
It contains some kind of location specifier # 31 "src/MyLib.hs, and the formatting of the example is broken.
In ok2 I've removed the second line from the example, but kept the :{ .. :} and the `<BLANKLINE>.
And that works again without problems.
{-# LANGUAGE CPP #-}
module MyLib where
{- | ok
#if __GLASGOW_HASKELL__ >= 900
>>> :{
show
a
:}
<interactive>:...: error: Variable not in scope: a
#else
>>> :{
show
b
:}
<interactive>:...: error: Variable not in scope: b
#endif
-}
ok :: t
ok = undefined
{- | broken
#if __GLASGOW_HASKELL__ >= 900
>>> :{
show
a
:}
<BLANKLINE>
<interactive>:...: error: Variable not in scope: a
#else
>>> :{
show
b
:}
<interactive>:...: error: Variable not in scope: b
#endif
-}
broken :: t
broken = undefined
{- | ok2
#if __GLASGOW_HASKELL__ >= 900
>>> :{
show a
:}
<BLANKLINE>
<interactive>:...: error: Variable not in scope: a
#else
>>> :{
show
b
:}
<interactive>:...: error: Variable not in scope: b
#endif
-}
ok2 :: t
ok2 = undefined
Tested with: haddock-2.24.0, ghc-8.10.2 and ghc-9.0.1
I've narrowed the testcase to:
{- |
#if 0
>>> :{
foo
bar
:}
<BLANKLINE>
baz
#endif
-}
broken :: t
broken = undefined
This renders as:
# 31 "src/MyLib.hs"
Where 31 is the line number of the line after the #endif.
And this also happens with haddock-2.25.0.
Ok this has nothing to do with the specific contents on #if block, but only with its length.
If you provide cpp with:
{-# LANGUAGE CPP #-}
module MyLib where
{- | foo
#if 0
1
2
3
4
5
6
#endif
-}
broken :: t
broken = undefined
{- | bar
#if 0
1
2
3
4
5
#endif
-}
notBroken :: t
notBroken = undefined
it will ouput:
# 1 "MyLib.hs"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "MyLib.hs"
{-# LANGUAGE CPP #-}
module MyLib where
{- | foo
# 13 "MyLib.hs"
-}
broken :: t
broken = undefined
{- | bar
-}
notBroken :: t
notBroken = undefined
And we can see that the #if 0 block is 5 lines or less cpp will output the just enough empty lines so the line numbers of the code after it matches exactly.
But when the block is longer then 5 lines it decides to generate a linemarker instead.
# 13 "MyLib.hs"
See: https://gcc.gnu.org/onlinedocs/gcc-11.2.0/cpp/Preprocessor-Output.html
Now (gnu?) cpp has an option
-P
Inhibit generation of linemarkers in the output from the preprocessor. This might be useful when running the > preprocessor on something that is not C code, and will be sent to a program which might be confused by the linemarkers.
And adding a {-# OPTIONS_GHC -optP -P #-} does prevent the issue.
But then line numbers in error messages don't match the user sourcecode anymore.
Maybe we could teach haddock to understand or simply skip over these linemarkers?