haddock icon indicating copy to clipboard operation
haddock copied to clipboard

Weird interaction between CPP #if blocks and haddock comments

Open leonschoorl opened this issue 4 years ago • 2 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

leonschoorl avatar Apr 20 '21 16:04 leonschoorl

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.

leonschoorl avatar Apr 21 '21 10:04 leonschoorl

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?

leonschoorl avatar Jul 30 '21 14:07 leonschoorl