Convert diagnostics in `GHC.Rename.*` modules to proper `TcRnMessage`
Short Summary: Get rid of the usages of TcRnUnknownMessage
in GHC.Rename.*
modules.
Do so by creating new type constructors for the TcRnMessage
type, specific to
the diagnostics you are converting.
Long summary
There are various occurrences of TcRnUnknownMessage
in the following modules:
-
GHC.Rename.Bind
(11) -
GHC.Rename.Env
(11) -
GHC.Rename.Expr
(13) -
GHC.Rename.HsType
(16) -
GHC.Rename.Module
(18) -
GHC.Rename.Names
(16) -
GHC.Rename.Pat
(1) -
GHC.Rename.Splice
(8) -
GHC.Rename.Unbound
(2) -
GHC.Rename.Utils
(11)
These occurrences takes a SDoc
as input and produces a TcRnMessage
, but they carry
very little information and structure with them, which means that GHC API users can't
meaningfully derive much from the SDoc
unless it's parsed or analysed.
We would like to replace these with new type constructors to be added to the TcRnMessage
type, which would give us a precise (and structured) semantic of the diagnostics in question.
Example
To give you a concrete example of a successfully ported message, we want to go from something like this:
localPatternSynonymErr :: TcRnMessage
localPatternSynonymErr
= TcRnUnknownMessage $ mkPlainError noHints $
hang (text "Illegal pattern synonym declaration for" <+> quotes (ppr rdrname))
2 (text "Pattern synonym declarations are only valid at top level")
to something like this:
localPatternSynonymErr = TcRnIllegalPatSynDecl rdrname
Where TcRnIllegalPatSynDecl
is defined as:
data TcRnMessage =
...
| TcRnIllegalPatSynDecl :: !(LIdP GhcPs) -> TcRnMessage
...
Possible plan of action
What follows is a possible plan of action, to guide the developer working on this:
-
Add as many type constructors as needed to
GHC.Tc.Errors.Types.TcRnMessage
. If possible, try to document these following the schema used for the existing ones. It might be possible there is already a type constructor readily available to use, so check for its presence. -
This is a good time to grep the codebase for the text of the diagnostics you want to port, which should hopefully yield a number of test cases. You can later run the testsuite on those tests locally (e.g.
./hadrian/build test --only=...
), to verify the final output didn't change. Occasionally the final output might change, for example if new hints are added. Don't be afraid to amend the tests in case that happens, and use your best judgement. The goal is to port existing messages to a new infrastructure, not necessarily improve GHC's diagnostic English prose. That can happen at a later stage. -
Occasionally some instances of
TcRnUnknownMessage
might be wrappingSDoc
s which are generated from otherSDoc
s (perhaps even passed as input in a function that produces yet anotherSDoc
). All those littleSDoc
fragments should be converted into proper Haskell types so that we would be passing to new type constructors not one or moreSDoc
s but richer Haskell types (random examples:Name
,OccName
,HsBind GhcPs
, etc). -
In rare circumstances it might be not feasible to pass a proper Haskell type, maybe due to some nasty cyclic import. If that happens, it's passable (but not ideal) to do something like this:
... TcRnNewConstructor :: Outputable a => ComplexTypeThatTakesAn a -> ... -> TcRnMessage
-
GHC will now complain that the
instance Diagnostic TcRnMessage
insideGHC.Tc.Errors.Ppr
is not complete, if new type constructors were added. ThediagnosticMessage
,diagnosticReason
anddiagnosticHints
will need to handle the new type constructors. -
Implement
diagnosticMessage
by copying the original textual representation of the diagnostic message from the ported module. If the textual representation is using external data (i.e. local binds in that module) add the necessary type arguments to the constructor to "carry the data over". Again, follow the existing patterns. -
Implement
diagnosticReason
by copying the originalDiagnosticReason(s)
from the original module, where the message was produced originally. -
Implement
diagnosticHints
by copying, once again, what the original diagnostic in ported module is doing. If there are no hints, returnnoHints
. Be aware that hints might be tucked and hidden into the English prose of the diagnostic you are porting, in which case you might have to create a brand newGhcHint
type constructor if none of the existing ones captures the hint.