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