What should it mean for a record selector to be "covered"?

Florian Ragwitz florian.ragwitz at gmail.com
Sun Mar 23 19:11:26 UTC 2025


Let's say you have

    data Foo = Foo { fooX, fooY :: Int }

For coverage reporting, HPC creates a coverage box for each top-level
definition. These boxes will be ticked appropriately at runtime when
the fooX and fooY bindings are used directly.

However, they won't be ticked if you pattern match on the constructor, such as

  f (Foo x y) = ...
  f Foo{ fooX = fooX } = ...
  f Foo{ fooX = x } = ...
  f Foo{ fooX } = ...
  f Foo{..} = ...

and variations thereof.

This is understandable, but can lead to some undesirable or unexpected
behaviour for users of RecordWildCards or RecordPuns especially. E.g.
https://gitlab.haskell.org/ghc/ghc/-/issues/17834 and
https://github.com/PostgREST/postgrest/issues/2627#issuecomment-1435642297.

GHC already seems to acknowledge this, at least to some extent, via
its treatment of unused top-level binding warnings when using
RecordWildCards, RecordPuns, or record patterns, where

  f Foo{ fooX } = ...
or
  f Foo{ fooX = x } = ...

will not emit a -Wunused-top-binds warning for fooX while still
emitting one for fooY, and where

  f Foo{..} = ...

will not emit any -Wunused-top-bind warnings at all.

Given these observations, and assuming that the current HPC behaviour
is undesirable, I’d like some guidance on what the exact desired
behavior for HPC coverage should be for record selectors. In
particular:

* Should a record selector be considered "covered" simply by being
  bound in a pattern match?
* Or should it only be marked as covered if the bound symbol is actually used?

If the latter is preferable, would a change that initially marks them
as covered upon binding (thereby aligning HPC with the treatment of
unused binding warnings) be acceptable as an interim step toward the
larger goal?

Thanks for your time and insights!


More information about the ghc-devs mailing list