Continue refactoring the core-to-core pipeline
[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         printErrorsAndWarnings, 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,
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 Control.Monad
43 import System.Exit      ( ExitCode(..), exitWith )
44 import Data.List
45 import System.IO
46
47 -- -----------------------------------------------------------------------------
48 -- Basic error messages: just render a message with a source location.
49
50 type Message = SDoc
51
52 pprMessageBag :: Bag Message -> SDoc
53 pprMessageBag msgs = vcat (punctuate blankLine (bagToList msgs))
54
55 data Severity
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 = printErrs (mkLocMessage span msg $ defaultErrStyle)
71
72
73 -- -----------------------------------------------------------------------------
74 -- Collecting up messages for later ordering and printing.
75
76 data ErrMsg = ErrMsg { 
77         errMsgSpans     :: [SrcSpan],
78         errMsgContext   :: PrintUnqualified,
79         errMsgShortDoc  :: Message,
80         errMsgExtraInfo :: Message
81         }
82         -- The SrcSpan is used for sorting errors into line-number order
83
84 instance Show ErrMsg where
85     show em = showSDoc (errMsgShortDoc em)
86
87 type WarnMsg = ErrMsg
88
89 -- A short (one-line) error message, with context to tell us whether
90 -- to qualify names in the message or not.
91 mkErrMsg :: SrcSpan -> PrintUnqualified -> Message -> ErrMsg
92 mkErrMsg locn print_unqual msg
93   = ErrMsg { errMsgSpans = [locn], errMsgContext = print_unqual
94            , errMsgShortDoc = msg, errMsgExtraInfo = empty }
95
96 -- Variant that doesn't care about qualified/unqualified names
97 mkPlainErrMsg :: SrcSpan -> Message -> ErrMsg
98 mkPlainErrMsg locn msg
99   = ErrMsg { errMsgSpans = [locn], errMsgContext = alwaysQualify
100            , errMsgShortDoc = msg, errMsgExtraInfo = empty }
101
102 -- A long (multi-line) error message, with context to tell us whether
103 -- to qualify names in the message or not.
104 mkLongErrMsg :: SrcSpan -> PrintUnqualified -> Message -> Message -> ErrMsg
105 mkLongErrMsg locn print_unqual msg extra 
106  = ErrMsg { errMsgSpans = [locn], errMsgContext = print_unqual
107           , errMsgShortDoc = msg, errMsgExtraInfo = extra }
108
109 mkWarnMsg :: SrcSpan -> PrintUnqualified -> Message -> WarnMsg
110 mkWarnMsg = mkErrMsg
111
112 mkLongWarnMsg :: SrcSpan -> PrintUnqualified -> Message -> Message -> ErrMsg
113 mkLongWarnMsg = mkLongErrMsg
114
115 -- Variant that doesn't care about qualified/unqualified names
116 mkPlainWarnMsg :: SrcSpan -> Message -> ErrMsg
117 mkPlainWarnMsg locn msg = mkWarnMsg locn alwaysQualify msg
118
119 type Messages = (Bag WarnMsg, Bag ErrMsg)
120
121 type WarningMessages = Bag WarnMsg
122 type ErrorMessages   = Bag ErrMsg
123
124 emptyMessages :: Messages
125 emptyMessages = (emptyBag, emptyBag)
126
127 warnIsErrorMsg :: ErrMsg
128 warnIsErrorMsg = mkPlainErrMsg noSrcSpan (text "\nFailing due to -Werror.\n")
129
130 errorsFound :: DynFlags -> Messages -> Bool
131 -- The dyn-flags are used to see if the user has specified
132 -- -Werror, which says that warnings should be fatal
133 errorsFound dflags (warns, errs) 
134   | dopt Opt_WarnIsError dflags = not (isEmptyBag errs) || not (isEmptyBag warns)
135   | otherwise                   = not (isEmptyBag errs)
136
137 printErrorsAndWarnings :: DynFlags -> Messages -> IO ()
138 printErrorsAndWarnings dflags (warns, errs)
139   | no_errs && no_warns = return ()
140   | no_errs             = do printBagOfWarnings dflags warns
141                              when (dopt Opt_WarnIsError dflags) $
142                                  errorMsg dflags $
143                                      text "\nFailing due to -Werror.\n"
144                           -- Don't print any warnings if there are errors
145   | otherwise           = printBagOfErrors dflags errs
146   where
147     no_warns = isEmptyBag warns
148     no_errs  = isEmptyBag errs
149
150 printBagOfErrors :: DynFlags -> Bag ErrMsg -> IO ()
151 printBagOfErrors dflags bag_of_errors
152   = sequence_   [ let style = mkErrStyle unqual
153                   in log_action dflags SevError s style (d $$ e)
154                 | ErrMsg { errMsgSpans = s:_,
155                            errMsgShortDoc = d,
156                            errMsgExtraInfo = e,
157                            errMsgContext = unqual } <- sorted_errs ]
158     where
159       bag_ls      = bagToList bag_of_errors
160       sorted_errs = sortLe occ'ed_before bag_ls
161
162       occ'ed_before err1 err2 = 
163          case compare (head (errMsgSpans err1)) (head (errMsgSpans err2)) of
164                 LT -> True
165                 EQ -> True
166                 GT -> False
167
168 printBagOfWarnings :: DynFlags -> Bag ErrMsg -> IO ()
169 printBagOfWarnings dflags bag_of_warns
170   = sequence_   [ let style = mkErrStyle unqual
171                   in log_action dflags SevWarning s style (d $$ e)
172                 | ErrMsg { errMsgSpans = s:_,
173                            errMsgShortDoc = d,
174                            errMsgExtraInfo = e,
175                            errMsgContext = unqual } <- sorted_errs ]
176     where
177       bag_ls      = bagToList bag_of_warns
178       sorted_errs = sortLe occ'ed_before bag_ls
179
180       occ'ed_before err1 err2 = 
181          case compare (head (errMsgSpans err1)) (head (errMsgSpans err2)) of
182                 LT -> True
183                 EQ -> True
184                 GT -> False
185
186 ghcExit :: DynFlags -> Int -> IO ()
187 ghcExit dflags val
188   | val == 0  = exitWith ExitSuccess
189   | otherwise = do errorMsg dflags (text "\nCompilation had errors\n\n")
190                    exitWith (ExitFailure val)
191
192 doIfSet :: Bool -> IO () -> IO ()
193 doIfSet flag action | flag      = action
194                     | otherwise = return ()
195
196 doIfSet_dyn :: DynFlags -> DynFlag -> IO () -> IO()
197 doIfSet_dyn dflags flag action | dopt flag dflags = action
198                                | otherwise        = return ()
199
200 -- -----------------------------------------------------------------------------
201 -- Dumping
202
203 dumpIfSet :: Bool -> String -> SDoc -> IO ()
204 dumpIfSet flag hdr doc
205   | not flag   = return ()
206   | otherwise  = printDump (mkDumpDoc hdr doc)
207
208 dumpIfSet_dyn :: DynFlags -> DynFlag -> String -> SDoc -> IO ()
209 dumpIfSet_dyn dflags flag hdr doc
210   | dopt flag dflags || verbosity dflags >= 4 
211   = dumpSDoc dflags flag hdr doc
212   | otherwise
213   = return ()
214
215 dumpIfSet_dyn_or :: DynFlags -> [DynFlag] -> String -> SDoc -> IO ()
216 dumpIfSet_dyn_or dflags flags hdr doc
217   | or [dopt flag dflags | flag <- flags]
218   || verbosity dflags >= 4 
219   = printDump (mkDumpDoc hdr doc)
220   | otherwise = return ()
221
222 mkDumpDoc :: String -> SDoc -> SDoc
223 mkDumpDoc hdr doc 
224    = vcat [blankLine,
225            line <+> text hdr <+> line,
226            doc,
227            blankLine]
228      where 
229         line = text (replicate 20 '=')
230
231
232 -- | Write out a dump.
233 --      If --dump-to-file is set then this goes to a file.
234 --      otherwise emit to stdout.
235 dumpSDoc :: DynFlags -> DynFlag -> String -> SDoc -> IO ()
236 dumpSDoc dflags dflag hdr doc
237  = do   let mFile       = chooseDumpFile dflags dflag
238         case mFile of
239                 -- write the dump to a file
240                 --      don't add the header in this case, we can see what kind
241                 --      of dump it is from the filename.
242                 Just fileName
243                  -> do  handle  <- openFile fileName AppendMode
244                         hPrintDump handle doc
245                         hClose handle
246
247                 -- write the dump to stdout
248                 Nothing
249                  -> do  printDump (mkDumpDoc hdr doc)
250
251
252 -- | Choose where to put a dump file based on DynFlags
253 --
254 chooseDumpFile :: DynFlags -> DynFlag -> Maybe String
255 chooseDumpFile dflags dflag
256
257         -- dump file location is being forced
258         --      by the --ddump-file-prefix flag.
259         | dumpToFile
260         , Just prefix   <- dumpPrefixForce dflags
261         = Just $ prefix ++ (beautifyDumpName dflag)
262
263         -- dump file location chosen by DriverPipeline.runPipeline
264         | dumpToFile
265         , Just prefix   <- dumpPrefix dflags
266         = Just $ prefix ++ (beautifyDumpName dflag)
267
268         -- we haven't got a place to put a dump file.
269         | otherwise
270         = Nothing
271
272         where   dumpToFile = dopt Opt_DumpToFile dflags
273
274
275 -- | Build a nice file name from name of a DynFlag constructor
276 beautifyDumpName :: DynFlag -> String
277 beautifyDumpName dflag
278  = let  str     = show dflag
279         cut     = if isPrefixOf "Opt_D_" str
280                          then drop 6 str
281                          else str
282         dash    = map   (\c -> case c of
283                                 '_'     -> '-'
284                                 _       -> c)
285                         cut
286    in   dash
287
288
289 -- -----------------------------------------------------------------------------
290 -- Outputting messages from the compiler
291
292 -- We want all messages to go through one place, so that we can
293 -- redirect them if necessary.  For example, when GHC is used as a
294 -- library we might want to catch all messages that GHC tries to
295 -- output and do something else with them.
296
297 ifVerbose :: DynFlags -> Int -> IO () -> IO ()
298 ifVerbose dflags val act
299   | verbosity dflags >= val = act
300   | otherwise               = return ()
301
302 putMsg :: DynFlags -> Message -> IO ()
303 putMsg dflags msg = log_action dflags SevInfo noSrcSpan defaultUserStyle msg
304
305 errorMsg :: DynFlags -> Message -> IO ()
306 errorMsg dflags msg = log_action dflags SevError noSrcSpan defaultErrStyle msg
307
308 fatalErrorMsg :: DynFlags -> Message -> IO ()
309 fatalErrorMsg dflags msg = log_action dflags SevFatal noSrcSpan defaultErrStyle msg
310
311 compilationProgressMsg :: DynFlags -> String -> IO ()
312 compilationProgressMsg dflags msg
313   = ifVerbose dflags 1 (log_action dflags SevInfo noSrcSpan defaultUserStyle (text msg))
314
315 showPass :: DynFlags -> String -> IO ()
316 showPass dflags what 
317   = ifVerbose dflags 2 (log_action dflags SevInfo noSrcSpan defaultUserStyle (text "***" <+> text what <> colon))
318
319 debugTraceMsg :: DynFlags -> Int -> Message -> IO ()
320 debugTraceMsg dflags val msg
321   = ifVerbose dflags val (log_action dflags SevInfo noSrcSpan defaultDumpStyle msg)
322
323 \end{code}