Start support for coloured SDoc output.
[ghc-hetmet.git] / compiler / main / ErrUtils.lhs
1 %
2 % (c) The AQUA Project, Glasgow University, 1994-1998
3 %
4 \section[ErrsUtils]{Utilities for error reporting}
5
6 \begin{code}
7 module ErrUtils (
8         Message, mkLocMessage, printError, pprMessageBag,
9         Severity(..),
10
11         ErrMsg, WarnMsg,
12         ErrorMessages, WarningMessages,
13         errMsgSpans, errMsgContext, errMsgShortDoc, errMsgExtraInfo,
14         Messages, errorsFound, emptyMessages,
15         mkErrMsg, mkPlainErrMsg, mkLongErrMsg, mkWarnMsg, mkPlainWarnMsg,
16         printBagOfErrors, printBagOfWarnings,
17         warnIsErrorMsg, mkLongWarnMsg,
18
19         ghcExit,
20         doIfSet, doIfSet_dyn, 
21         dumpIfSet, dumpIfSet_dyn, dumpIfSet_dyn_or,
22         mkDumpDoc, dumpSDoc,
23
24         --  * Messages during compilation
25         putMsg, putMsgWith,
26         errorMsg,
27         fatalErrorMsg,
28         compilationProgressMsg,
29         showPass,
30         debugTraceMsg,  
31     ) where
32
33 #include "HsVersions.h"
34
35 import Bag              ( Bag, bagToList, isEmptyBag, emptyBag )
36 import Util             ( sortLe )
37 import Outputable
38 import SrcLoc
39 import DynFlags         ( DynFlags(..), DynFlag(..), dopt )
40 import StaticFlags      ( opt_ErrorSpans )
41
42 import System.Exit      ( ExitCode(..), exitWith )
43 import Data.List
44 import System.IO
45
46 -- -----------------------------------------------------------------------------
47 -- Basic error messages: just render a message with a source location.
48
49 type Message = SDoc
50
51 pprMessageBag :: Bag Message -> SDoc
52 pprMessageBag msgs = vcat (punctuate blankLine (bagToList msgs))
53
54 data Severity
55   = SevOutput
56   | SevInfo
57   | SevWarning
58   | SevError
59   | SevFatal
60
61 mkLocMessage :: SrcSpan -> Message -> Message
62 mkLocMessage locn msg
63   | opt_ErrorSpans = hang (ppr locn <> colon) 4 msg
64   | otherwise      = hang (ppr (srcSpanStart locn) <> colon) 4 msg
65   -- always print the location, even if it is unhelpful.  Error messages
66   -- are supposed to be in a standard format, and one without a location
67   -- would look strange.  Better to say explicitly "<no location info>".
68
69 printError :: SrcSpan -> Message -> IO ()
70 printError span msg =
71   printErrs (mkLocMessage span msg) defaultErrStyle
72
73
74 -- -----------------------------------------------------------------------------
75 -- Collecting up messages for later ordering and printing.
76
77 data ErrMsg = ErrMsg { 
78         errMsgSpans     :: [SrcSpan],
79         errMsgContext   :: PrintUnqualified,
80         errMsgShortDoc  :: Message,
81         errMsgExtraInfo :: Message
82         }
83         -- The SrcSpan is used for sorting errors into line-number order
84
85 instance Show ErrMsg where
86     show em = showSDoc (errMsgShortDoc em)
87
88 type WarnMsg = ErrMsg
89
90 -- A short (one-line) error message, with context to tell us whether
91 -- to qualify names in the message or not.
92 mkErrMsg :: SrcSpan -> PrintUnqualified -> Message -> ErrMsg
93 mkErrMsg locn print_unqual msg
94   = ErrMsg { errMsgSpans = [locn], errMsgContext = print_unqual
95            , errMsgShortDoc = msg, errMsgExtraInfo = empty }
96
97 -- Variant that doesn't care about qualified/unqualified names
98 mkPlainErrMsg :: SrcSpan -> Message -> ErrMsg
99 mkPlainErrMsg locn msg
100   = ErrMsg { errMsgSpans = [locn], errMsgContext = alwaysQualify
101            , errMsgShortDoc = msg, errMsgExtraInfo = empty }
102
103 -- A long (multi-line) error message, with context to tell us whether
104 -- to qualify names in the message or not.
105 mkLongErrMsg :: SrcSpan -> PrintUnqualified -> Message -> Message -> ErrMsg
106 mkLongErrMsg locn print_unqual msg extra 
107  = ErrMsg { errMsgSpans = [locn], errMsgContext = print_unqual
108           , errMsgShortDoc = msg, errMsgExtraInfo = extra }
109
110 mkWarnMsg :: SrcSpan -> PrintUnqualified -> Message -> WarnMsg
111 mkWarnMsg = mkErrMsg
112
113 mkLongWarnMsg :: SrcSpan -> PrintUnqualified -> Message -> Message -> ErrMsg
114 mkLongWarnMsg = mkLongErrMsg
115
116 -- Variant that doesn't care about qualified/unqualified names
117 mkPlainWarnMsg :: SrcSpan -> Message -> ErrMsg
118 mkPlainWarnMsg locn msg = mkWarnMsg locn alwaysQualify msg
119
120 type Messages = (Bag WarnMsg, Bag ErrMsg)
121
122 type WarningMessages = Bag WarnMsg
123 type ErrorMessages   = Bag ErrMsg
124
125 emptyMessages :: Messages
126 emptyMessages = (emptyBag, emptyBag)
127
128 warnIsErrorMsg :: ErrMsg
129 warnIsErrorMsg = mkPlainErrMsg noSrcSpan (text "\nFailing due to -Werror.")
130
131 errorsFound :: DynFlags -> Messages -> Bool
132 errorsFound _dflags (_warns, errs) = not (isEmptyBag errs)
133
134 printBagOfErrors :: DynFlags -> Bag ErrMsg -> IO ()
135 printBagOfErrors dflags bag_of_errors = 
136   printMsgBag dflags bag_of_errors SevError
137
138 printBagOfWarnings :: DynFlags -> Bag WarnMsg -> IO ()
139 printBagOfWarnings dflags bag_of_warns = 
140   printMsgBag dflags bag_of_warns SevWarning
141
142 printMsgBag :: DynFlags -> Bag ErrMsg -> Severity -> IO ()
143 printMsgBag dflags bag sev
144   = sequence_   [ let style = mkErrStyle unqual
145                   in log_action dflags sev s style (d $$ e)
146                 | ErrMsg { errMsgSpans = s:_,
147                            errMsgShortDoc = d,
148                            errMsgExtraInfo = e,
149                            errMsgContext = unqual } <- sorted_errs ]
150     where
151       bag_ls      = bagToList bag
152       sorted_errs = sortLe occ'ed_before bag_ls
153
154       occ'ed_before err1 err2 = 
155          case compare (head (errMsgSpans err1)) (head (errMsgSpans err2)) of
156                 LT -> True
157                 EQ -> True
158                 GT -> False
159
160 ghcExit :: DynFlags -> Int -> IO ()
161 ghcExit dflags val
162   | val == 0  = exitWith ExitSuccess
163   | otherwise = do errorMsg dflags (text "\nCompilation had errors\n\n")
164                    exitWith (ExitFailure val)
165
166 doIfSet :: Bool -> IO () -> IO ()
167 doIfSet flag action | flag      = action
168                     | otherwise = return ()
169
170 doIfSet_dyn :: DynFlags -> DynFlag -> IO () -> IO()
171 doIfSet_dyn dflags flag action | dopt flag dflags = action
172                                | otherwise        = return ()
173
174 -- -----------------------------------------------------------------------------
175 -- Dumping
176
177 dumpIfSet :: Bool -> String -> SDoc -> IO ()
178 dumpIfSet flag hdr doc
179   | not flag   = return ()
180   | otherwise  = printDump (mkDumpDoc hdr doc)
181
182 dumpIfSet_dyn :: DynFlags -> DynFlag -> String -> SDoc -> IO ()
183 dumpIfSet_dyn dflags flag hdr doc
184   | dopt flag dflags || verbosity dflags >= 4 
185   = dumpSDoc dflags flag hdr doc
186   | otherwise
187   = return ()
188
189 dumpIfSet_dyn_or :: DynFlags -> [DynFlag] -> String -> SDoc -> IO ()
190 dumpIfSet_dyn_or dflags flags hdr doc
191   | or [dopt flag dflags | flag <- flags]
192   || verbosity dflags >= 4 
193   = printDump (mkDumpDoc hdr doc)
194   | otherwise = return ()
195
196 mkDumpDoc :: String -> SDoc -> SDoc
197 mkDumpDoc hdr doc 
198    = vcat [blankLine,
199            line <+> text hdr <+> line,
200            doc,
201            blankLine]
202      where 
203         line = text (replicate 20 '=')
204
205
206 -- | Write out a dump.
207 --      If --dump-to-file is set then this goes to a file.
208 --      otherwise emit to stdout.
209 dumpSDoc :: DynFlags -> DynFlag -> String -> SDoc -> IO ()
210 dumpSDoc dflags dflag hdr doc
211  = do   let mFile       = chooseDumpFile dflags dflag
212         case mFile of
213                 -- write the dump to a file
214                 --      don't add the header in this case, we can see what kind
215                 --      of dump it is from the filename.
216                 Just fileName
217                  -> do  handle  <- openFile fileName AppendMode
218                         hPrintDump handle doc
219                         hClose handle
220
221                 -- write the dump to stdout
222                 Nothing
223                  -> do  printDump (mkDumpDoc hdr doc)
224
225
226 -- | Choose where to put a dump file based on DynFlags
227 --
228 chooseDumpFile :: DynFlags -> DynFlag -> Maybe String
229 chooseDumpFile dflags dflag
230
231         -- dump file location is being forced
232         --      by the --ddump-file-prefix flag.
233         | dumpToFile
234         , Just prefix   <- dumpPrefixForce dflags
235         = Just $ prefix ++ (beautifyDumpName dflag)
236
237         -- dump file location chosen by DriverPipeline.runPipeline
238         | dumpToFile
239         , Just prefix   <- dumpPrefix dflags
240         = Just $ prefix ++ (beautifyDumpName dflag)
241
242         -- we haven't got a place to put a dump file.
243         | otherwise
244         = Nothing
245
246         where   dumpToFile = dopt Opt_DumpToFile dflags
247
248
249 -- | Build a nice file name from name of a DynFlag constructor
250 beautifyDumpName :: DynFlag -> String
251 beautifyDumpName dflag
252  = let  str     = show dflag
253         cut     = if isPrefixOf "Opt_D_" str
254                          then drop 6 str
255                          else str
256         dash    = map   (\c -> case c of
257                                 '_'     -> '-'
258                                 _       -> c)
259                         cut
260    in   dash
261
262
263 -- -----------------------------------------------------------------------------
264 -- Outputting messages from the compiler
265
266 -- We want all messages to go through one place, so that we can
267 -- redirect them if necessary.  For example, when GHC is used as a
268 -- library we might want to catch all messages that GHC tries to
269 -- output and do something else with them.
270
271 ifVerbose :: DynFlags -> Int -> IO () -> IO ()
272 ifVerbose dflags val act
273   | verbosity dflags >= val = act
274   | otherwise               = return ()
275
276 putMsg :: DynFlags -> Message -> IO ()
277 putMsg dflags msg = log_action dflags SevInfo noSrcSpan defaultUserStyle msg
278
279 putMsgWith :: DynFlags -> PrintUnqualified -> Message -> IO ()
280 putMsgWith dflags print_unqual msg
281   = log_action dflags SevInfo noSrcSpan sty msg
282   where
283     sty = mkUserStyle print_unqual AllTheWay
284
285 errorMsg :: DynFlags -> Message -> IO ()
286 errorMsg dflags msg = log_action dflags SevError noSrcSpan defaultErrStyle msg
287
288 fatalErrorMsg :: DynFlags -> Message -> IO ()
289 fatalErrorMsg dflags msg = log_action dflags SevFatal noSrcSpan defaultErrStyle msg
290
291 compilationProgressMsg :: DynFlags -> String -> IO ()
292 compilationProgressMsg dflags msg
293   = ifVerbose dflags 1 (log_action dflags SevOutput noSrcSpan defaultUserStyle (text msg))
294
295 showPass :: DynFlags -> String -> IO ()
296 showPass dflags what 
297   = ifVerbose dflags 2 (log_action dflags SevInfo noSrcSpan defaultUserStyle (text "***" <+> text what <> colon))
298
299 debugTraceMsg :: DynFlags -> Int -> Message -> IO ()
300 debugTraceMsg dflags val msg
301   = ifVerbose dflags val (log_action dflags SevInfo noSrcSpan defaultDumpStyle msg)
302
303 \end{code}