ghc-clippy-plugin: Override GHC error messages to the user's liking

[ bsd3, compiler-plugin, development, library ] [ Propose Tags ]


  • Clippy


Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Versions [RSS]
Change log
Dependencies base (>=4.7 && <5), dhall (>=1.30.0 && <1.34), ghc (>=8.8.2 && <8.11), text (>= && <1.3), text-icu (>=0.7.0 && <0.8), text-regex-replace (>=0.1.1 && <0.2) [details]
License BSD-3-Clause
Copyright 2020 Artur Gajowy
Author Artur Gajowy
Revised Revision 1 made by arturgajowy at 2020-06-18T19:03:45Z
Category Development, Compiler Plugin
Home page
Bug tracker
Source repo head: git clone
Uploaded by arturgajowy at 2020-06-12T18:44:23Z
Reverse Dependencies 1 direct, 0 indirect [details]
Downloads 266 total (6 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs not available [build log]
All reported builds failed as of 2020-06-12 [all 2 reports]

Readme for ghc-clippy-plugin-

[back to package description]

GHC Clippy Plugin

Build status License

A helpful companion to GHC.

Overrides GHC error and warning messages, to the user's liking.

Configured using (how else!) regexps. Tested with stack and ghcid.


Left: without Clippy.

Right: with Clippy, using the sample config.

But, why?

For all kinds of reasons:

  • making GHC messages more terse or more verbose
  • adding more context for beginners
  • stripping confusing / duplicated / rarely useful context for everyone
  • prototyping improvements for ghc error output
  • ever wanted GHC to speak in emoji? 😈
  • ... or in your mother tongue?
  • ... or in mathematical notation?


  1. Add the ghc-clippy-plugin dependency to your project
  2. Pass -fplugin=Clippy in GHC options

E.g. for stack:

# file: package.yaml
 - ghc-clippy-plugin

- -fplugin=Clippy
  1. When building, you should see the following warning:
ghc-clippy-plugin: warning:
    Clippy plugin couldn't start. Cause:
    ./.clippy.dhall: openFile: does not exist (No such file or directory)

Make sure there was anything to compile (change a .hs file) if the warning wasn't there.

Save the sample config as .clippy.dhall in the project's root dir (or - more precisely - the current directory GHC is going to use)

  1. Put an error somewhere:
oops = print mempty

With the sample config, this should output:

./app/Main.hs:19:14-19: error:
    Type variable β€˜a0’ is ambiguous in β€˜mempty’.
    Can't pick an instance for β€˜(Monoid a0)’.
    Maybe-fix: add type annotations to disambiguate.
    More info: compile with -fprint-potential-instances.
19 | oops = print mempty
  1. Enjoy the much terser output and tweak it to your heart's content! 😁

Tweaking the config


For --file-watch to pick up config changes, add in package.yaml:

- .clippy.dhall

Use stack build --file-watch. Make sure to have some errors handy! :)


For ghcid to reload after config change, run it with --reload=.clippy.dhall. I tend to put the above line in ./.ghcid.

Ghcid may terminate if there are compile errors on startup. If that's the case, remove your errors until ghcid starts successfully :)

With the above, for error messages, ghcid picks up .clippy.dhall changes immediately.

For warnings, one needs to trigger recompilation of the file triggering them. One way around that is to enable -Werror for the period of config tweaking. (I put mine in ./.ghci.)

Error message structure, section markers

In GHC, each error/warning message contains 3 sections in its ErrDoc ('Important', 'Context', and 'Supplementary'). Each section contains a list of MsgDoc-s.

Before replacing the message text, Clippy wraps each section, and each of their MsgDoc-s, with markers. This allows for more precise match targeting, including MsgDoc/section ends.

To see the structure of the replaced error message, comment out the marker removing rule in the config.

  rules =
    [ -- rule "(>>[ICS]>)|(<[ICS]<<)|(>[ICS]>)|(<[ICS]<)" ""
    , rule "..." "..."
    -- ...

Comment out all rules to see the structure of the original message. Sample result:

./app/Main.hs:27:11: error:
    β€’ >>I>
      No instance for (Num a) arising from a use of β€˜+’
      Possible fix:
        add (Num a) to the context of
          the type signature for:
            bar :: forall a. a -> a -> a
    β€’ >>C>
      In the expression: a + b
      In an equation for β€˜bar’: bar a b = a + b
    β€’ >>S>
27 | bar a b = a + b

Replace rules can span across multiple messages in a section, but can't cross section boundaries. For example, the following rule will remove the entire 'Context' section:

  rule "(?s)>>C>.*?<C<<" "" -- notice the (?s) - "dot-all" regex flag

All-whitespace lines are removed from all the messages.

Rule matching order

Replacement rules are applied in reverse order of the rules list in config. This means the most generic and least selective rules should go at the top of the file. In particular, the marker removing rule - which should be applied last - should be the first rule in every config.


I'd like to thank the authors of


  • resolve the config from the first directory above that contains .clippy.dhall
  • fall back to ~/.config/clippy.dhall and then to ~/.clippy.dhall
  • cache the config instead of re-parsing for every module
  • ...?