Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haddock link to functions in non-imported modules

In module B I have documentation with a link 'A.foo', linking to the foo member of module A. In module A I import module B. Haddock renders this as a link to A.html#t:foo, namely pointing at the type foo (which does not exist) not the function foo, which is at A.html#v:foo.

  • Why does Haddock link to t: for variables that start with a lower case letter? Is that a bug? For 'A.Foo' I can see that it could be a type or a constructor, so there are namespacing issues. For foo it seems a variable is at least most plausible.
  • Is there any way to fake a link? I am writing this in code samples, so I need it to be rendered as foo. I tried anchors, but they render as the module name, and for direct hyperlinks you have no control over the displayed text.
  • I considered a post processor (replacing t:[a-z] with v:), but that requires a custom Setup.hs which causes problems and is quite ugly.
  • I couldn't find any Haddock command line flags to obtain a more reasonable behavior, such as specifying that foo is a variable.
  • I can't add an import of A to B without introducing circular imports, which is vile to add purely for documentation.

I am running into this problem in the Shake documentation, where as an example removeFilesAfter does not get the right link.

like image 653
Neil Mitchell Avatar asked Jul 28 '13 20:07

Neil Mitchell


2 Answers

I can partially answer the the first question (Why?); not sure if it is a bug or desired behaviour.

When haddock resolves references in LexParseRn.rename, it tries to look up the identifier in the environment (via lookupGRE_RdrName). This ought to fail. Next it looks as what the thing could mean (using dataTcOccs from GHC’s RnEnv). The relevant lines are:

dataTcOccs :: RdrName -> [RdrName]
-- Return both the given name and the same name promoted to the TcClsName
-- namespace.  This is useful when we aren't sure which we are looking at.
dataTcOccs rdr_name
  [...]
  | isDataOcc occ || isVarOcc occ
  = [rdr_name, rdr_name_tc]
  [...]
  where
    occ = rdrNameOcc rdr_name
    rdr_name_tc = setRdrNameSpace rdr_name tcName

so it returns the name first interpreted as whatever it was before (likely a link to a value), and then interpreted as a type constructor. How can a regular name be a type constructor? My guess is that this was added when TypeOperators were reformed in GHC 7.6, which now do share the namespace with value-level operators.

Then haddock matches on the result: If the first one is a type constructor, use that, otherwise use the second. So either it was a type constructor before, then this is used. Or it was not, but then the modified version generated by dataTcOccs is to be used.

It seems to me that haddock should just always use the first option here, and the code is just a mislead copy from how multiple results are used when they can actually be resolved.

like image 163
Joachim Breitner Avatar answered Nov 01 '22 14:11

Joachim Breitner


This was a Haddock bug #228 and Neil's Haddock bug #253 and the fix has been upstream for few months. You can build GHC HEAD and rebuild your documentation or wait for 7.8 and do it then.

like image 44
Mateusz Kowalczyk Avatar answered Nov 01 '22 14:11

Mateusz Kowalczyk