summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfredo Di Napoli <alfredo@well-typed.com>2021-04-06 16:27:14 +0200
committerRichard Eisenberg <rae@richarde.dev>2021-04-28 15:30:28 -0400
commit43e930d3a664061c05490ed763cc4ea4bad9038e (patch)
tree6862c0e53b2c872b50f8698f63bfb36e8d103e17
parentd2399a46a01a6e46c831c19e797e656a0b8ca16d (diff)
downloadhaskell-wip/adinapoli-diagnostics-adts-lean.tar.gz
Add GhcMessage and ancillary typeswip/adinapoli-diagnostics-adts-lean
This commit adds GhcMessage and ancillary (PsMessage, TcRnMessage, ..) types. These types will be expanded to represent more errors generated by different subsystems within GHC. Right now, they are underused, but more will come in the glorious future. See https://gitlab.haskell.org/ghc/ghc/-/wikis/Errors-as-(structured)-values for a design overview. Along the way, lots of other things had to happen: * Adds Semigroup and Monoid instance for Bag * Fixes #19746 by parsing OPTIONS_GHC pragmas into Located Strings. See GHC.Parser.Header.toArgs (moved from GHC.Utils.Misc, where it didn't belong anyway). * Addresses (but does not completely fix) #19709, now reporting desugarer warnings and errors appropriately for TH splices. Not done: reporting type-checker warnings for TH splices. * Some small refactoring around Safe Haskell inference, in order to keep separate classes of messages separate. * Some small refactoring around initDsTc, in order to keep separate classes of messages separate. * Separate out the generation of messages (that is, the construction of the text block) from the wrapping of messages (that is, assigning a SrcSpan). This is more modular than the previous design, which mixed the two. Close #19746. This was a collaborative effort by Alfredo di Napoli and Richard Eisenberg, with a key assist on #19746 by Iavor Diatchki. Metric Increase: MultiLayerModules
-rw-r--r--compiler/GHC.hs23
-rw-r--r--compiler/GHC/Data/Bag.hs7
-rw-r--r--compiler/GHC/Driver/Backpack.hs10
-rw-r--r--compiler/GHC/Driver/Env.hs4
-rw-r--r--compiler/GHC/Driver/Env/Types.hs7
-rw-r--r--compiler/GHC/Driver/Errors.hs49
-rw-r--r--compiler/GHC/Driver/Errors/Ppr.hs45
-rw-r--r--compiler/GHC/Driver/Errors/Types.hs120
-rw-r--r--compiler/GHC/Driver/Main.hs232
-rw-r--r--compiler/GHC/Driver/Make.hs113
-rw-r--r--compiler/GHC/Driver/MakeFile.hs6
-rw-r--r--compiler/GHC/Driver/Monad.hs20
-rw-r--r--compiler/GHC/Driver/Pipeline.hs24
-rw-r--r--compiler/GHC/HsToCore.hs25
-rw-r--r--compiler/GHC/HsToCore/Errors/Ppr.hs11
-rw-r--r--compiler/GHC/HsToCore/Errors/Types.hs10
-rw-r--r--compiler/GHC/HsToCore/Monad.hs30
-rw-r--r--compiler/GHC/HsToCore/Types.hs3
-rw-r--r--compiler/GHC/Iface/Load.hs4
-rw-r--r--compiler/GHC/Iface/Rename.hs32
-rw-r--r--compiler/GHC/Parser/Errors.hs8
-rw-r--r--compiler/GHC/Parser/Errors/Ppr.hs30
-rw-r--r--compiler/GHC/Parser/Errors/Types.hs9
-rw-r--r--compiler/GHC/Parser/Header.hs146
-rw-r--r--compiler/GHC/Parser/PostProcess.hs8
-rw-r--r--compiler/GHC/Runtime/Eval.hs5
-rw-r--r--compiler/GHC/Tc/Deriv.hs2
-rw-r--r--compiler/GHC/Tc/Errors.hs25
-rw-r--r--compiler/GHC/Tc/Errors/Ppr.hs10
-rw-r--r--compiler/GHC/Tc/Errors/Types.hs12
-rw-r--r--compiler/GHC/Tc/Gen/Foreign.hs2
-rw-r--r--compiler/GHC/Tc/Gen/Splice.hs68
-rw-r--r--compiler/GHC/Tc/Module.hs36
-rw-r--r--compiler/GHC/Tc/Solver.hs8
-rw-r--r--compiler/GHC/Tc/Types.hs20
-rw-r--r--compiler/GHC/Tc/Utils/Backpack.hs7
-rw-r--r--compiler/GHC/Tc/Utils/Monad.hs74
-rw-r--r--compiler/GHC/Tc/Validity.hs3
-rw-r--r--compiler/GHC/Types/Error.hs272
-rw-r--r--compiler/GHC/Types/SourceError.hs28
-rw-r--r--compiler/GHC/Utils/Error.hs96
-rw-r--r--compiler/GHC/Utils/Misc.hs64
-rw-r--r--compiler/ghc.cabal.in7
-rw-r--r--ghc/GHCi/UI.hs40
-rw-r--r--testsuite/tests/driver/StringListOptions.hs3
-rw-r--r--testsuite/tests/driver/T2464.stderr2
-rw-r--r--testsuite/tests/driver/T2499.stderr6
-rw-r--r--testsuite/tests/driver/all.T1
-rw-r--r--testsuite/tests/ghc-api/T18522-dbg-ppr.hs4
-rw-r--r--testsuite/tests/parser/should_compile/T16619.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/T16270.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/readFail044.stderr4
-rw-r--r--testsuite/tests/parser/should_run/CountAstDeps.stdout12
-rw-r--r--testsuite/tests/parser/should_run/CountParserDeps.stdout10
-rw-r--r--testsuite/tests/regalloc/regalloc_unit_tests.hs2
-rw-r--r--testsuite/tests/safeHaskell/check/Check05.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags18.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags19.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags22.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags23.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags25.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags26.stderr2
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags28.stderr8
-rw-r--r--testsuite/tests/safeHaskell/flags/SafeFlags29.stderr8
-rw-r--r--testsuite/tests/safeHaskell/overlapping/SH_Overlap11.stderr2
-rw-r--r--testsuite/tests/safeHaskell/overlapping/SH_Overlap7.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/SafeInfered05.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/SafeWarn01.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeInfered11.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeInfered12.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn01.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn02.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn03.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn04.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn05.stderr4
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn06.stderr2
-rw-r--r--testsuite/tests/safeHaskell/safeInfered/UnsafeWarn07.stderr2
-rw-r--r--testsuite/tests/th/T19709a.hs9
-rw-r--r--testsuite/tests/th/T19709a.stderr6
-rw-r--r--testsuite/tests/th/T19709b.hs12
-rw-r--r--testsuite/tests/th/T19709b.stderr6
-rw-r--r--testsuite/tests/th/T19709c.hs9
-rw-r--r--testsuite/tests/th/T19709c.stderr10
-rw-r--r--testsuite/tests/th/T19709d.hs6
-rw-r--r--testsuite/tests/th/T19709d.stderr16
-rw-r--r--testsuite/tests/th/all.T4
-rw-r--r--utils/check-exact/Parsers.hs27
-rw-r--r--utils/check-exact/Preprocess.hs20
88 files changed, 1333 insertions, 653 deletions
diff --git a/compiler/GHC.hs b/compiler/GHC.hs
index 59f49453ed..2a75c2b840 100644
--- a/compiler/GHC.hs
+++ b/compiler/GHC.hs
@@ -307,6 +307,7 @@ import GHC.Driver.Phases ( Phase(..), isHaskellSrcFilename
, isSourceFilename, startPhase )
import GHC.Driver.Env
import GHC.Driver.Errors
+import GHC.Driver.Errors.Types
import GHC.Driver.CmdLine
import GHC.Driver.Session
import qualified GHC.Driver.Session as Session
@@ -338,7 +339,6 @@ import GHC.Iface.Load ( loadSysInterface )
import GHC.Hs
import GHC.Builtin.Types.Prim ( alphaTyVars )
import GHC.Iface.Tidy
-import GHC.Data.Bag ( listToBag )
import GHC.Data.StringBuffer
import GHC.Data.FastString
import qualified GHC.LanguageExtensions as LangExt
@@ -382,6 +382,7 @@ import GHC.Types.Name.Set
import GHC.Types.Name.Reader
import GHC.Types.SourceError
import GHC.Types.SafeHaskell
+import GHC.Types.Error hiding ( getMessages, getErrorMessages )
import GHC.Types.Fixity
import GHC.Types.Target
import GHC.Types.Basic
@@ -390,7 +391,6 @@ import GHC.Types.Name.Env
import GHC.Types.Name.Ppr
import GHC.Types.TypeEnv
import GHC.Types.SourceFile
-import GHC.Types.Error ( DiagnosticMessage )
import GHC.Unit
import GHC.Unit.Env
@@ -912,9 +912,11 @@ checkNewInteractiveDynFlags logger dflags0 = do
-- We currently don't support use of StaticPointers in expressions entered on
-- the REPL. See #12356.
if xopt LangExt.StaticPointers dflags0
- then do liftIO $ printOrThrowDiagnostics logger dflags0 $ listToBag
- [mkPlainMsgEnvelope dflags0 Session.WarningWithoutFlag interactiveSrcSpan
- $ text "StaticPointers is not supported in GHCi interactive expressions."]
+ then do liftIO $ printOrThrowDiagnostics logger dflags0 $ singleMessage
+ $ mkPlainMsgEnvelope dflags0 interactiveSrcSpan
+ $ GhcDriverMessage $ DriverUnknownMessage
+ $ mkPlainDiagnostic Session.WarningWithoutFlag
+ $ text "StaticPointers is not supported in GHCi interactive expressions."
return $ xopt_unset dflags0 LangExt.StaticPointers
else return dflags0
@@ -1505,7 +1507,7 @@ getNameToInstancesIndex :: GhcMonad m
-- if it is visible from at least one module in the list.
-> Maybe [Module] -- ^ modules to load. If this is not specified, we load
-- modules for everything that is in scope unqualified.
- -> m (Messages DiagnosticMessage, Maybe (NameEnv ([ClsInst], [FamInst])))
+ -> m (Messages TcRnMessage, Maybe (NameEnv ([ClsInst], [FamInst])))
getNameToInstancesIndex visible_mods mods_to_load = do
hsc_env <- getSession
liftIO $ runTcInteractive hsc_env $
@@ -1610,7 +1612,7 @@ getTokenStream mod = do
let startLoc = mkRealSrcLoc (mkFastString sourceFile) 1 1
case lexTokenStream (initParserOpts dflags) source startLoc of
POk _ ts -> return ts
- PFailed pst -> throwErrors (fmap mkParserErr (getErrorMessages pst))
+ PFailed pst -> throwErrors (foldPsMessages mkParserErr (getErrorMessages pst))
-- | Give even more information on the source than 'getTokenStream'
-- This function allows reconstructing the source completely with
@@ -1621,7 +1623,7 @@ getRichTokenStream mod = do
let startLoc = mkRealSrcLoc (mkFastString sourceFile) 1 1
case lexTokenStream (initParserOpts dflags) source startLoc of
POk _ ts -> return $ addSourceToTokens startLoc source ts
- PFailed pst -> throwErrors (fmap mkParserErr (getErrorMessages pst))
+ PFailed pst -> throwErrors (foldPsMessages mkParserErr (getErrorMessages pst))
-- | Given a source location and a StringBuffer corresponding to this
-- location, return a rich token stream with the source associated to the
@@ -1801,11 +1803,12 @@ parser str dflags filename =
PFailed pst ->
let (warns,errs) = getMessages pst in
- (fmap (mkParserWarn dflags) warns, Left (fmap mkParserErr errs))
+ (foldPsMessages (mkParserWarn dflags) warns
+ , Left (foldPsMessages mkParserErr errs))
POk pst rdr_module ->
let (warns,_) = getMessages pst in
- (fmap (mkParserWarn dflags) warns, Right rdr_module)
+ (foldPsMessages (mkParserWarn dflags) warns, Right rdr_module)
-- -----------------------------------------------------------------------------
-- | Find the package environment (if one exists)
diff --git a/compiler/GHC/Data/Bag.hs b/compiler/GHC/Data/Bag.hs
index e314309efc..338b463832 100644
--- a/compiler/GHC/Data/Bag.hs
+++ b/compiler/GHC/Data/Bag.hs
@@ -37,6 +37,7 @@ import Data.Maybe( mapMaybe )
import Data.List ( partition, mapAccumL )
import Data.List.NonEmpty ( NonEmpty(..) )
import qualified Data.Foldable as Foldable
+import qualified Data.Semigroup ( (<>) )
infixr 3 `consBag`
infixl 3 `snocBag`
@@ -343,3 +344,9 @@ instance IsList (Bag a) where
type Item (Bag a) = a
fromList = listToBag
toList = bagToList
+
+instance Semigroup (Bag a) where
+ (<>) = unionBags
+
+instance Monoid (Bag a) where
+ mempty = emptyBag
diff --git a/compiler/GHC/Driver/Backpack.hs b/compiler/GHC/Driver/Backpack.hs
index b781685e91..30289129c4 100644
--- a/compiler/GHC/Driver/Backpack.hs
+++ b/compiler/GHC/Driver/Backpack.hs
@@ -31,6 +31,7 @@ import GHC.Driver.Main
import GHC.Driver.Make
import GHC.Driver.Env
import GHC.Driver.Errors
+import GHC.Driver.Errors.Types
import GHC.Parser
import GHC.Parser.Header
@@ -107,7 +108,7 @@ doBackpack [src_filename] = do
buf <- liftIO $ hGetStringBuffer src_filename
let loc = mkRealSrcLoc (mkFastString src_filename) 1 1 -- TODO: not great
case unP parseBackpack (initParserState (initParserOpts dflags) buf loc) of
- PFailed pst -> throwErrors (fmap mkParserErr (getErrorMessages pst))
+ PFailed pst -> throwErrors (foldPsMessages mkParserErr (getErrorMessages pst))
POk _ pkgname_bkp -> do
-- OK, so we have an LHsUnit PackageName, but we want an
-- LHsUnit HsComponentId. So let's rename it.
@@ -808,9 +809,10 @@ summariseDecl _pn hsc_src lmodname@(L loc modname) Nothing
Nothing -- GHC API buffer support not supported
[] -- No exclusions
case r of
- Nothing -> throwOneError (mkPlainErrorMsgEnvelope loc
- (text "module" <+> ppr modname <+> text "was not found"))
- Just (Left err) -> throwErrors err
+ Nothing -> throwOneError $ mkPlainErrorMsgEnvelope loc $
+ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
+ (text "module" <+> ppr modname <+> text "was not found")
+ Just (Left err) -> throwErrors (fmap GhcDriverMessage err)
Just (Right summary) -> return summary
-- | Up until now, GHC has assumed a single compilation target per source file.
diff --git a/compiler/GHC/Driver/Env.hs b/compiler/GHC/Driver/Env.hs
index 3fff8ab65c..219e66106b 100644
--- a/compiler/GHC/Driver/Env.hs
+++ b/compiler/GHC/Driver/Env.hs
@@ -50,6 +50,7 @@ import GHC.Core.InstEnv ( ClsInst )
import GHC.Types.Annotations ( Annotation, AnnEnv, mkAnnEnv, plusAnnEnv )
import GHC.Types.CompleteMatch
+import GHC.Types.Error ( emptyMessages )
import GHC.Types.Name
import GHC.Types.Name.Env
import GHC.Types.TyThing
@@ -57,7 +58,6 @@ import GHC.Types.TyThing
import GHC.Builtin.Names ( gHC_PRIM )
import GHC.Data.Maybe
-import GHC.Data.Bag
import GHC.Utils.Outputable
import GHC.Utils.Monad
@@ -69,7 +69,7 @@ import Data.IORef
runHsc :: HscEnv -> Hsc a -> IO a
runHsc hsc_env (Hsc hsc) = do
- (a, w) <- hsc hsc_env emptyBag
+ (a, w) <- hsc hsc_env emptyMessages
printOrThrowDiagnostics (hsc_logger hsc_env) (hsc_dflags hsc_env) w
return a
diff --git a/compiler/GHC/Driver/Env/Types.hs b/compiler/GHC/Driver/Env/Types.hs
index d1fc22314a..d672de33e6 100644
--- a/compiler/GHC/Driver/Env/Types.hs
+++ b/compiler/GHC/Driver/Env/Types.hs
@@ -4,12 +4,13 @@ module GHC.Driver.Env.Types
, HscEnv(..)
) where
+import GHC.Driver.Errors.Types ( GhcMessage )
import {-# SOURCE #-} GHC.Driver.Hooks
import GHC.Driver.Session ( DynFlags, HasDynFlags(..) )
import GHC.Prelude
import GHC.Runtime.Context
import GHC.Runtime.Interpreter.Types ( Interp )
-import GHC.Types.Error ( WarningMessages )
+import GHC.Types.Error ( Messages )
import GHC.Types.Name.Cache
import GHC.Types.Target
import GHC.Types.TypeEnv
@@ -25,8 +26,8 @@ import Control.Monad ( ap )
import Control.Monad.IO.Class
import Data.IORef
--- | The Hsc monad: Passing an environment and warning state
-newtype Hsc a = Hsc (HscEnv -> WarningMessages -> IO (a, WarningMessages))
+-- | The Hsc monad: Passing an environment and diagnostic state
+newtype Hsc a = Hsc (HscEnv -> Messages GhcMessage -> IO (a, Messages GhcMessage))
deriving (Functor)
instance Applicative Hsc where
diff --git a/compiler/GHC/Driver/Errors.hs b/compiler/GHC/Driver/Errors.hs
index 362282d1b9..7afb0f3b26 100644
--- a/compiler/GHC/Driver/Errors.hs
+++ b/compiler/GHC/Driver/Errors.hs
@@ -1,29 +1,25 @@
module GHC.Driver.Errors (
printOrThrowDiagnostics
- , printBagOfErrors
+ , printMessages
, handleFlagWarnings
- , partitionMessageBag
+ , mkDriverPsHeaderMessage
) where
import GHC.Driver.Session
+import GHC.Driver.Errors.Types
import GHC.Data.Bag
-import GHC.Utils.Exception
-import GHC.Utils.Error ( formatBulleted, sortMsgBag, mkPlainMsgEnvelope )
-import GHC.Types.SourceError ( mkSrcErr )
import GHC.Prelude
+import GHC.Parser.Errors ( PsError(..) )
import GHC.Types.SrcLoc
+import GHC.Types.SourceError
import GHC.Types.Error
+import GHC.Utils.Error
import GHC.Utils.Outputable ( text, withPprStyle, mkErrStyle )
import GHC.Utils.Logger
import qualified GHC.Driver.CmdLine as CmdLine
--- | Partitions the messages and returns a tuple which first element are the warnings, and the
--- second the errors.
-partitionMessageBag :: Diagnostic e => Bag (MsgEnvelope e) -> (Bag (MsgEnvelope e), Bag (MsgEnvelope e))
-partitionMessageBag = partitionBag isWarningMessage
-
-printBagOfErrors :: Diagnostic a => Logger -> DynFlags -> Bag (MsgEnvelope a) -> IO ()
-printBagOfErrors logger dflags bag_of_errors
+printMessages :: Diagnostic a => Logger -> DynFlags -> Messages a -> IO ()
+printMessages logger dflags msgs
= sequence_ [ let style = mkErrStyle unqual
ctx = initSDocContext dflags style
in putLogMsg logger dflags (MCDiagnostic sev . diagnosticReason $ dia) s $
@@ -32,22 +28,35 @@ printBagOfErrors logger dflags bag_of_errors
errMsgDiagnostic = dia,
errMsgSeverity = sev,
errMsgContext = unqual } <- sortMsgBag (Just dflags)
- bag_of_errors ]
+ (getMessages msgs) ]
handleFlagWarnings :: Logger -> DynFlags -> [CmdLine.Warn] -> IO ()
handleFlagWarnings logger dflags warns = do
let -- It would be nicer if warns :: [Located SDoc], but that
-- has circular import problems.
- bag = listToBag [ mkPlainMsgEnvelope dflags reason loc (text warn)
+ bag = listToBag [ mkPlainMsgEnvelope dflags loc $
+ GhcDriverMessage $
+ DriverUnknownMessage $
+ mkPlainDiagnostic reason $
+ text warn
| CmdLine.Warn reason (L loc warn) <- warns ]
- printOrThrowDiagnostics logger dflags bag
+ printOrThrowDiagnostics logger dflags (mkMessages bag)
-- | Given a bag of diagnostics, turn them into an exception if
-- any has 'SevError', or print them out otherwise.
-printOrThrowDiagnostics :: Logger -> DynFlags -> Bag WarnMsg -> IO ()
-printOrThrowDiagnostics logger dflags warns
- | any ((==) SevError . errMsgSeverity) warns
- = throwIO (mkSrcErr warns)
+printOrThrowDiagnostics :: Logger -> DynFlags -> Messages GhcMessage -> IO ()
+printOrThrowDiagnostics logger dflags msgs
+ | errorsOrFatalWarningsFound msgs
+ = throwErrors msgs
| otherwise
- = printBagOfErrors logger dflags warns
+ = printMessages logger dflags msgs
+
+-- | Convert a 'PsError' into a wrapped 'DriverMessage'; use it
+-- for dealing with parse errors when the driver is doing dependency analysis.
+-- Defined here to avoid module loops between GHC.Driver.Error.Types and
+-- GHC.Driver.Error.Ppr
+mkDriverPsHeaderMessage :: PsError -> MsgEnvelope DriverMessage
+mkDriverPsHeaderMessage ps_err
+ = mkPlainErrorMsgEnvelope (errLoc ps_err) $
+ DriverPsHeaderMessage (errDesc ps_err) (errHints ps_err)
diff --git a/compiler/GHC/Driver/Errors/Ppr.hs b/compiler/GHC/Driver/Errors/Ppr.hs
new file mode 100644
index 0000000000..06ebe0be96
--- /dev/null
+++ b/compiler/GHC/Driver/Errors/Ppr.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE LambdaCase #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-} -- instance Diagnostic {DriverMessage, GhcMessage}
+
+module GHC.Driver.Errors.Ppr where
+
+import GHC.Prelude
+
+import GHC.Types.Error
+import GHC.Driver.Errors.Types
+import GHC.Parser.Errors.Ppr
+import GHC.Tc.Errors.Ppr ()
+import GHC.HsToCore.Errors.Ppr ()
+
+instance Diagnostic GhcMessage where
+ diagnosticMessage = \case
+ GhcPsMessage m
+ -> diagnosticMessage m
+ GhcTcRnMessage m
+ -> diagnosticMessage m
+ GhcDsMessage m
+ -> diagnosticMessage m
+ GhcDriverMessage m
+ -> diagnosticMessage m
+ GhcUnknownMessage m
+ -> diagnosticMessage m
+
+ diagnosticReason = \case
+ GhcPsMessage m
+ -> diagnosticReason m
+ GhcTcRnMessage m
+ -> diagnosticReason m
+ GhcDsMessage m
+ -> diagnosticReason m
+ GhcDriverMessage m
+ -> diagnosticReason m
+ GhcUnknownMessage m
+ -> diagnosticReason m
+
+instance Diagnostic DriverMessage where
+ diagnosticMessage (DriverUnknownMessage m) = diagnosticMessage m
+ diagnosticMessage (DriverPsHeaderMessage desc hints)
+ = mkSimpleDecorated $ pprPsError desc hints
+
+ diagnosticReason (DriverUnknownMessage m) = diagnosticReason m
+ diagnosticReason (DriverPsHeaderMessage {}) = ErrorWithoutFlag
diff --git a/compiler/GHC/Driver/Errors/Types.hs b/compiler/GHC/Driver/Errors/Types.hs
new file mode 100644
index 0000000000..017852fcbb
--- /dev/null
+++ b/compiler/GHC/Driver/Errors/Types.hs
@@ -0,0 +1,120 @@
+{-# LANGUAGE GADTs #-}
+
+module GHC.Driver.Errors.Types (
+ GhcMessage(..)
+ , DriverMessage(..), DriverMessages
+ , WarningMessages
+ , ErrorMessages
+ , WarnMsg
+ -- * Constructors
+ , ghcUnknownMessage
+ -- * Utility functions
+ , hoistTcRnMessage
+ , hoistDsMessage
+ , foldPsMessages
+ ) where
+
+import GHC.Prelude
+
+import Data.Typeable
+import GHC.Types.Error
+
+import GHC.Parser.Errors ( PsErrorDesc, PsHint )
+import GHC.Parser.Errors.Types ( PsMessage )
+import GHC.Tc.Errors.Types ( TcRnMessage )
+import GHC.HsToCore.Errors.Types ( DsMessage )
+import Data.Bifunctor
+
+-- | A collection of warning messages.
+-- /INVARIANT/: Each 'GhcMessage' in the collection should have 'SevWarning' severity.
+type WarningMessages = Messages GhcMessage
+
+-- | A collection of error messages.
+-- /INVARIANT/: Each 'GhcMessage' in the collection should have 'SevError' severity.
+type ErrorMessages = Messages GhcMessage
+
+-- | A single warning message.
+-- /INVARIANT/: It must have 'SevWarning' severity.
+type WarnMsg = MsgEnvelope GhcMessage
+
+
+{- Note [GhcMessage]
+~~~~~~~~~~~~~~~~~~~~
+
+We might need to report diagnostics (error and/or warnings) to the users. The
+'GhcMessage' type is the root of the diagnostic hierarchy.
+
+It's useful to have a separate type constructor for the different stages of
+the compilation pipeline. This is not just helpful for tools, as it gives a
+clear indication on where the error occurred exactly. Furthermore it increases
+the modularity amongst the different components of GHC (i.e. to avoid having
+"everything depend on everything else") and allows us to write separate
+functions that renders the different kind of messages.
+
+-}
+
+-- | The umbrella type that encompasses all the different messages that GHC
+-- might output during the different compilation stages. See
+-- Note [GhcMessage].
+data GhcMessage where
+ -- | A message from the parsing phase.
+ GhcPsMessage :: PsMessage -> GhcMessage
+ -- | A message from typecheck/renaming phase.
+ GhcTcRnMessage :: TcRnMessage -> GhcMessage
+ -- | A message from the desugaring (HsToCore) phase.
+ GhcDsMessage :: DsMessage -> GhcMessage
+ -- | A message from the driver.
+ GhcDriverMessage :: DriverMessage -> GhcMessage
+
+ -- | An \"escape\" hatch which can be used when we don't know the source of
+ -- the message or if the message is not one of the typed ones. The
+ -- 'Diagnostic' and 'Typeable' constraints ensure that if we /know/, at
+ -- pattern-matching time, the originating type, we can attempt a cast and
+ -- access the fully-structured error. This would be the case for a GHC
+ -- plugin that offers a domain-specific error type but that doesn't want to
+ -- place the burden on IDEs/application code to \"know\" it. The
+ -- 'Diagnostic' constraint ensures that worst case scenario we can still
+ -- render this into something which can be eventually converted into a
+ -- 'DecoratedSDoc'.
+ GhcUnknownMessage :: forall a. (Diagnostic a, Typeable a) => a -> GhcMessage
+
+-- | Creates a new 'GhcMessage' out of any diagnostic. This function is also
+-- provided to ease the integration of #18516 by allowing diagnostics to be
+-- wrapped into the general (but structured) 'GhcMessage' type, so that the
+-- conversion can happen gradually. This function should not be needed within
+-- GHC, as it would typically be used by plugin or library authors (see
+-- comment for the 'GhcUnknownMessage' type constructor)
+ghcUnknownMessage :: (Diagnostic a, Typeable a) => a -> GhcMessage
+ghcUnknownMessage = GhcUnknownMessage
+
+-- | Given a collection of @e@ wrapped in a 'Foldable' structure, converts it
+-- into 'Messages' via the supplied transformation function.
+foldPsMessages :: Foldable f
+ => (e -> MsgEnvelope PsMessage)
+ -> f e
+ -> Messages GhcMessage
+foldPsMessages f = foldMap (singleMessage . fmap GhcPsMessage . f)
+
+-- | Abstracts away the frequent pattern where we are calling 'ioMsgMaybe' on
+-- the result of 'IO (Messages TcRnMessage, a)'.
+hoistTcRnMessage :: Monad m => m (Messages TcRnMessage, a) -> m (Messages GhcMessage, a)
+hoistTcRnMessage = fmap (first (fmap GhcTcRnMessage))
+
+-- | Abstracts away the frequent pattern where we are calling 'ioMsgMaybe' on
+-- the result of 'IO (Messages DsMessage, a)'.
+hoistDsMessage :: Monad m => m (Messages DsMessage, a) -> m (Messages GhcMessage, a)
+hoistDsMessage = fmap (first (fmap GhcDsMessage))
+
+-- | A message from the driver.
+data DriverMessage
+ = DriverUnknownMessage !DiagnosticMessage
+ -- ^ Simply rewraps a generic 'DiagnosticMessage'. More
+ -- constructors will be added in the future (#18516).
+ | DriverPsHeaderMessage !PsErrorDesc ![PsHint]
+ -- ^ A parse error in parsing a Haskell file header during dependency
+ -- analysis
+
+-- | A collection of driver messages
+type DriverMessages = Messages DriverMessage
+
+-- | A message about Safe Haskell.
diff --git a/compiler/GHC/Driver/Main.hs b/compiler/GHC/Driver/Main.hs
index cac12cae50..c147733bb3 100644
--- a/compiler/GHC/Driver/Main.hs
+++ b/compiler/GHC/Driver/Main.hs
@@ -95,6 +95,7 @@ import GHC.Driver.Session
import GHC.Driver.Backend
import GHC.Driver.Env
import GHC.Driver.Errors
+import GHC.Driver.Errors.Types
import GHC.Driver.CodeOutput
import GHC.Driver.Config
import GHC.Driver.Hooks
@@ -144,6 +145,7 @@ import GHC.CoreToStg ( coreToStg )
import GHC.Parser.Errors
import GHC.Parser.Errors.Ppr
+import GHC.Parser.Errors.Types
import GHC.Parser
import GHC.Parser.Lexer as Lexer
@@ -188,7 +190,8 @@ import GHC.Types.SourceError
import GHC.Types.SafeHaskell
import GHC.Types.ForeignStubs
import GHC.Types.Var.Env ( emptyTidyEnv )
-import GHC.Types.Error hiding ( getMessages )
+import GHC.Types.Error hiding ( getMessages )
+import qualified GHC.Types.Error as Error.Types
import GHC.Types.Fixity.Env
import GHC.Types.CostCentre
import GHC.Types.IPE
@@ -206,7 +209,6 @@ import GHC.Utils.Fingerprint ( Fingerprint )
import GHC.Utils.Panic
import GHC.Utils.Error
import GHC.Utils.Outputable
-import GHC.Utils.Exception
import GHC.Utils.Misc
import GHC.Utils.Logger
import GHC.Utils.TmpFs
@@ -265,14 +267,14 @@ newHscEnv dflags = do
-- -----------------------------------------------------------------------------
-getWarnings :: Hsc WarningMessages
-getWarnings = Hsc $ \_ w -> return (w, w)
+getDiagnostics :: Hsc (Messages GhcMessage)
+getDiagnostics = Hsc $ \_ w -> return (w, w)
-clearWarnings :: Hsc ()
-clearWarnings = Hsc $ \_ _ -> return ((), emptyBag)
+clearDiagnostics :: Hsc ()
+clearDiagnostics = Hsc $ \_ _ -> return ((), emptyMessages)
-logDiagnostics :: Bag (MsgEnvelope DiagnosticMessage) -> Hsc ()
-logDiagnostics w = Hsc $ \_ w0 -> return ((), w0 `unionBags` w)
+logDiagnostics :: Messages GhcMessage -> Hsc ()
+logDiagnostics w = Hsc $ \_ w0 -> return ((), w0 `unionMessages` w)
getHscEnv :: Hsc HscEnv
getHscEnv = Hsc $ \e w -> return (e, w)
@@ -281,32 +283,32 @@ handleWarnings :: Hsc ()
handleWarnings = do
dflags <- getDynFlags
logger <- getLogger
- w <- getWarnings
+ w <- getDiagnostics
liftIO $ printOrThrowDiagnostics logger dflags w
- clearWarnings
+ clearDiagnostics
-- | log warning in the monad, and if there are errors then
-- throw a SourceError exception.
logWarningsReportErrors :: (Bag PsWarning, Bag PsError) -> Hsc ()
logWarningsReportErrors (warnings,errors) = do
dflags <- getDynFlags
- let warns = fmap (mkParserWarn dflags) warnings
- errs = fmap mkParserErr errors
+ let warns = foldPsMessages (mkParserWarn dflags) warnings
+ errs = foldPsMessages mkParserErr errors
logDiagnostics warns
- when (not $ isEmptyBag errs) $ throwErrors errs
+ when (not $ isEmptyMessages errs) $ throwErrors errs
-- | Log warnings and throw errors, assuming the messages
-- contain at least one error (e.g. coming from PFailed)
handleWarningsThrowErrors :: (Bag PsWarning, Bag PsError) -> Hsc a
handleWarningsThrowErrors (warnings, errors) = do
dflags <- getDynFlags
- let warns = fmap (mkParserWarn dflags) warnings
- errs = fmap mkParserErr errors
+ let warns = foldPsMessages (mkParserWarn dflags) warnings
+ errs = foldPsMessages mkParserErr errors
logDiagnostics warns
logger <- getLogger
- let (wWarns, wErrs) = partitionMessageBag warns
- liftIO $ printBagOfErrors logger dflags wWarns
- throwErrors (unionBags errs wErrs)
+ let (wWarns, wErrs) = partitionMessages warns
+ liftIO $ printMessages logger dflags wWarns
+ throwErrors $ errs `unionMessages` wErrs
-- | Deal with errors and warnings returned by a compilation step
--
@@ -324,21 +326,21 @@ handleWarningsThrowErrors (warnings, errors) = do
-- 2. If there are no error messages, but the second result indicates failure
-- there should be warnings in the first result. That is, if the action
-- failed, it must have been due to the warnings (i.e., @-Werror@).
-ioMsgMaybe :: IO (Messages DiagnosticMessage, Maybe a) -> Hsc a
+ioMsgMaybe :: IO (Messages GhcMessage, Maybe a) -> Hsc a
ioMsgMaybe ioA = do
(msgs, mb_r) <- liftIO ioA
let (warns, errs) = partitionMessages msgs
logDiagnostics warns
case mb_r of
Nothing -> throwErrors errs
- Just r -> ASSERT( isEmptyBag errs ) return r
+ Just r -> ASSERT( isEmptyMessages errs ) return r
-- | like ioMsgMaybe, except that we ignore error messages and return
-- 'Nothing' instead.
-ioMsgMaybe' :: IO (Messages DiagnosticMessage, Maybe a) -> Hsc (Maybe a)
+ioMsgMaybe' :: IO (Messages GhcMessage, Maybe a) -> Hsc (Maybe a)
ioMsgMaybe' ioA = do
(msgs, mb_r) <- liftIO $ ioA
- logDiagnostics (getWarningMessages msgs)
+ logDiagnostics (mkMessages $ getWarningMessages msgs)
return mb_r
-- -----------------------------------------------------------------------------
@@ -348,12 +350,12 @@ hscTcRnLookupRdrName :: HscEnv -> LocatedN RdrName -> IO [Name]
hscTcRnLookupRdrName hsc_env0 rdr_name
= runInteractiveHsc hsc_env0 $
do { hsc_env <- getHscEnv
- ; ioMsgMaybe $ tcRnLookupRdrName hsc_env rdr_name }
+ ; ioMsgMaybe $ hoistTcRnMessage $ tcRnLookupRdrName hsc_env rdr_name }
hscTcRcLookupName :: HscEnv -> Name -> IO (Maybe TyThing)
hscTcRcLookupName hsc_env0 name = runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
- ioMsgMaybe' $ tcRnLookupName hsc_env name
+ ioMsgMaybe' $ hoistTcRnMessage $ tcRnLookupName hsc_env name
-- ignore errors: the only error we're likely to get is
-- "name not found", and the Maybe in the return type
-- is used to indicate that.
@@ -363,23 +365,23 @@ hscTcRnGetInfo :: HscEnv -> Name
hscTcRnGetInfo hsc_env0 name
= runInteractiveHsc hsc_env0 $
do { hsc_env <- getHscEnv
- ; ioMsgMaybe' $ tcRnGetInfo hsc_env name }
+ ; ioMsgMaybe' $ hoistTcRnMessage $ tcRnGetInfo hsc_env name }
hscIsGHCiMonad :: HscEnv -> String -> IO Name
hscIsGHCiMonad hsc_env name
- = runHsc hsc_env $ ioMsgMaybe $ isGHCiMonad hsc_env name
+ = runHsc hsc_env $ ioMsgMaybe $ hoistTcRnMessage $ isGHCiMonad hsc_env name
hscGetModuleInterface :: HscEnv -> Module -> IO ModIface
hscGetModuleInterface hsc_env0 mod = runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
- ioMsgMaybe $ getModuleInterface hsc_env mod
+ ioMsgMaybe $ hoistTcRnMessage $ getModuleInterface hsc_env mod
-- -----------------------------------------------------------------------------
-- | Rename some import declarations
hscRnImportDecls :: HscEnv -> [LImportDecl GhcPs] -> IO GlobalRdrEnv
hscRnImportDecls hsc_env0 import_decls = runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
- ioMsgMaybe $ tcRnImportDecls hsc_env import_decls
+ ioMsgMaybe $ hoistTcRnMessage $ tcRnImportDecls hsc_env import_decls
-- -----------------------------------------------------------------------------
-- | parse a file, returning the abstract syntax
@@ -417,7 +419,10 @@ hscParse' mod_summary
PFailed pst ->
handleWarningsThrowErrors (getMessages pst)
POk pst rdr_module -> do
- let (warns, errs) = bimap (fmap (mkParserWarn dflags)) (fmap mkParserErr) (getMessages pst)
+ let (warns, errs) =
+ bimap (foldPsMessages (mkParserWarn dflags))
+ (foldPsMessages mkParserErr)
+ (getMessages pst)
logDiagnostics warns
liftIO $ dumpIfSet_dyn logger dflags Opt_D_dump_parsed "Parser"
FormatHaskell (ppr rdr_module)
@@ -427,7 +432,7 @@ hscParse' mod_summary
rdr_module)
liftIO $ dumpIfSet_dyn logger dflags Opt_D_source_stats "Source Statistics"
FormatText (ppSourceStats False rdr_module)
- when (not $ isEmptyBag errs) $ throwErrors errs
+ when (not $ isEmptyMessages errs) $ throwErrors errs
-- To get the list of extra source files, we take the list
-- that the parser gave us,
@@ -537,7 +542,7 @@ hsc_typecheck keep_rn mod_summary mb_rdr_module = do
keep_rn' = gopt Opt_WriteHie dflags || keep_rn
MASSERT( isHomeModule home_unit outer_mod )
tc_result <- if hsc_src == HsigFile && not (isHoleModule inner_mod)
- then ioMsgMaybe $ tcRnInstantiateSignature hsc_env outer_mod' real_loc
+ then ioMsgMaybe $ hoistTcRnMessage $ tcRnInstantiateSignature hsc_env outer_mod' real_loc
else
do hpm <- case mb_rdr_module of
Just hpm -> return hpm
@@ -545,7 +550,7 @@ hsc_typecheck keep_rn mod_summary mb_rdr_module = do
tc_result0 <- tcRnModule' mod_summary keep_rn' hpm
if hsc_src == HsigFile
then do (iface, _, _) <- liftIO $ hscSimpleIface hsc_env tc_result0 Nothing
- ioMsgMaybe $
+ ioMsgMaybe $ hoistTcRnMessage $
tcRnMergeSignatures hsc_env hpm tc_result0 iface
else return tc_result0
-- TODO are we extracting anything when we merely instantiate a signature?
@@ -564,18 +569,20 @@ tcRnModule' sum save_rn_syntax mod = do
-- -Wmissing-safe-haskell-mode
when (not (safeHaskellModeEnabled dflags)
&& wopt Opt_WarnMissingSafeHaskellMode dflags) $
- logDiagnostics $ unitBag $
- mkPlainMsgEnvelope dflags reason (getLoc (hpm_module mod)) $
- warnMissingSafeHaskellMode
+ logDiagnostics $ singleMessage $
+ mkPlainMsgEnvelope dflags (getLoc (hpm_module mod)) $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic reason warnMissingSafeHaskellMode
tcg_res <- {-# SCC "Typecheck-Rename" #-}
- ioMsgMaybe $
+ ioMsgMaybe $ hoistTcRnMessage $
tcRnModule hsc_env sum
save_rn_syntax mod
-- See Note [Safe Haskell Overlapping Instances Implementation]
-- although this is used for more than just that failure case.
- (tcSafeOK, whyUnsafe) <- liftIO $ readIORef (tcg_safeInfer tcg_res)
+ tcSafeOK <- liftIO $ readIORef (tcg_safe_infer tcg_res)
+ whyUnsafe <- liftIO $ readIORef (tcg_safe_infer_reasons tcg_res)
let allSafeOK = safeInferred dflags && tcSafeOK
-- end of the safe haskell line, how to respond to user?
@@ -587,20 +594,22 @@ tcRnModule' sum save_rn_syntax mod = do
-- module (could be) safe, throw warning if needed
else do
tcg_res' <- hscCheckSafeImports tcg_res
- safe <- liftIO $ fst <$> readIORef (tcg_safeInfer tcg_res')
+ safe <- liftIO $ readIORef (tcg_safe_infer tcg_res')
when safe $
case wopt Opt_WarnSafe dflags of
True
| safeHaskell dflags == Sf_Safe -> return ()
- | otherwise -> (logDiagnostics $ unitBag $
- mkPlainMsgEnvelope dflags (WarningWithFlag Opt_WarnSafe)
- (warnSafeOnLoc dflags) $
+ | otherwise -> (logDiagnostics $ singleMessage $
+ mkPlainMsgEnvelope dflags (warnSafeOnLoc dflags) $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic (WarningWithFlag Opt_WarnSafe) $
errSafe tcg_res')
False | safeHaskell dflags == Sf_Trustworthy &&
wopt Opt_WarnTrustworthySafe dflags ->
- (logDiagnostics $ unitBag $
- mkPlainMsgEnvelope dflags (WarningWithFlag Opt_WarnTrustworthySafe)
- (trustworthyOnLoc dflags) $
+ (logDiagnostics $ singleMessage $
+ mkPlainMsgEnvelope dflags (trustworthyOnLoc dflags) $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic (WarningWithFlag Opt_WarnTrustworthySafe) $
errTwthySafe tcg_res')
False -> return ()
return tcg_res'
@@ -620,9 +629,9 @@ hscDesugar hsc_env mod_summary tc_result =
hscDesugar' :: ModLocation -> TcGblEnv -> Hsc ModGuts
hscDesugar' mod_location tc_result = do
hsc_env <- getHscEnv
- r <- ioMsgMaybe $
- {-# SCC "deSugar" #-}
- deSugar hsc_env mod_location tc_result
+ r <- ioMsgMaybe $ hoistDsMessage $
+ {-# SCC "deSugar" #-}
+ deSugar hsc_env mod_location tc_result
-- always check -Werror after desugaring, this is the last opportunity for
-- warnings to arise before the backend.
@@ -1177,7 +1186,7 @@ hscCheckSafeImports tcg_env = do
case safeLanguageOn dflags of
True -> do
-- XSafe: we nuke user written RULES
- logDiagnostics $ warns dflags (tcg_rules tcg_env')
+ logDiagnostics $ fmap GhcDriverMessage $ warns dflags (tcg_rules tcg_env')
return tcg_env' { tcg_rules = [] }
False
-- SafeInferred: user defined RULES, so not safe
@@ -1188,11 +1197,13 @@ hscCheckSafeImports tcg_env = do
| otherwise
-> return tcg_env'
- warns dflags rules = listToBag $ map (warnRules dflags) rules
+ warns dflags rules = mkMessages $ listToBag $ map (warnRules dflags) rules
- warnRules :: DynFlags -> LRuleDecl GhcTc -> MsgEnvelope DiagnosticMessage
+ warnRules :: DynFlags -> LRuleDecl GhcTc -> MsgEnvelope DriverMessage
warnRules df (L loc (HsRule { rd_name = n })) =
- mkPlainMsgEnvelope df WarningWithoutFlag (locA loc) $
+ mkPlainMsgEnvelope df (locA loc) $
+ DriverUnknownMessage $
+ mkPlainDiagnostic WarningWithoutFlag $
text "Rule \"" <> ftext (snd $ unLoc n) <> text "\" ignored" $+$
text "User defined rules are disabled under Safe Haskell"
@@ -1218,33 +1229,33 @@ checkSafeImports tcg_env
-- We want to use the warning state specifically for detecting if safe
-- inference has failed, so store and clear any existing warnings.
- oldErrs <- getWarnings
- clearWarnings
+ oldErrs <- getDiagnostics
+ clearDiagnostics
-- Check safe imports are correct
safePkgs <- S.fromList <$> mapMaybeM checkSafe safeImps
- safeErrs <- getWarnings
- clearWarnings
+ safeErrs <- getDiagnostics
+ clearDiagnostics
-- Check non-safe imports are correct if inferring safety
-- See the Note [Safe Haskell Inference]
(infErrs, infPkgs) <- case (safeInferOn dflags) of
- False -> return (emptyBag, S.empty)
+ False -> return (emptyMessages, S.empty)
True -> do infPkgs <- S.fromList <$> mapMaybeM checkSafe regImps
- infErrs <- getWarnings
- clearWarnings
+ infErrs <- getDiagnostics
+ clearDiagnostics
return (infErrs, infPkgs)
-- restore old errors
logDiagnostics oldErrs
- case (isEmptyBag safeErrs) of
+ case (isEmptyMessages safeErrs) of
-- Failed safe check
- False -> liftIO . throwIO . mkSrcErr $ safeErrs
+ False -> liftIO . throwErrors $ safeErrs
-- Passed safe check
True -> do
- let infPassed = isEmptyBag infErrs
+ let infPassed = isEmptyMessages infErrs
tcg_env' <- case (not infPassed) of
True -> markUnsafeInfer tcg_env infErrs
False -> return tcg_env
@@ -1268,9 +1279,11 @@ checkSafeImports tcg_env
cond' :: ImportedModsVal -> ImportedModsVal -> Hsc ImportedModsVal
cond' v1 v2
| imv_is_safe v1 /= imv_is_safe v2
- = throwOneError $ mkPlainErrorMsgEnvelope (imv_span v1)
- (text "Module" <+> ppr (imv_name v1) <+>
- (text $ "is imported both as a safe and unsafe import!"))
+ = throwOneError $
+ mkPlainErrorMsgEnvelope (imv_span v1) $
+ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
+ text "Module" <+> ppr (imv_name v1) <+>
+ (text $ "is imported both as a safe and unsafe import!")
| otherwise
= return v1
@@ -1299,15 +1312,15 @@ hscCheckSafe hsc_env m l = runHsc hsc_env $ do
dflags <- getDynFlags
pkgs <- snd `fmap` hscCheckSafe' m l
when (packageTrustOn dflags) $ checkPkgTrust pkgs
- errs <- getWarnings
- return $ isEmptyBag errs
+ errs <- getDiagnostics
+ return $ isEmptyMessages errs
-- | Return if a module is trusted and the pkgs it depends on to be trusted.
hscGetSafe :: HscEnv -> Module -> SrcSpan -> IO (Bool, Set UnitId)
hscGetSafe hsc_env m l = runHsc hsc_env $ do
(self, pkgs) <- hscCheckSafe' m l
- good <- isEmptyBag `fmap` getWarnings
- clearWarnings -- don't want them printed...
+ good <- isEmptyMessages `fmap` getDiagnostics
+ clearDiagnostics -- don't want them printed...
let pkgs' | Just p <- self = S.insert p pkgs
| otherwise = pkgs
return (good, pkgs')
@@ -1336,9 +1349,11 @@ hscCheckSafe' m l = do
iface <- lookup' m
case iface of
-- can't load iface to check trust!
- Nothing -> throwOneError $ mkPlainErrorMsgEnvelope l
- $ text "Can't load the interface file for" <+> ppr m
- <> text ", to check that it can be safely imported"
+ Nothing -> throwOneError $
+ mkPlainErrorMsgEnvelope l $
+ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
+ text "Can't load the interface file for" <+> ppr m
+ <> text ", to check that it can be safely imported"
-- got iface, check trust
Just iface' ->
@@ -1355,10 +1370,10 @@ hscCheckSafe' m l = do
&& safeLanguageOn dflags
&& trust == Sf_SafeInferred
then inferredImportWarn dflags
- else emptyBag
+ else emptyMessages
-- General errors we throw but Safe errors we log
errs = case (safeM, safeP) of
- (True, True ) -> emptyBag
+ (True, True ) -> emptyMessages
(True, False) -> pkgTrustErr
(False, _ ) -> modTrustErr
in do
@@ -1368,24 +1383,29 @@ hscCheckSafe' m l = do
where
state = hsc_units hsc_env
- inferredImportWarn dflags = unitBag
- $ mkShortMsgEnvelope dflags (WarningWithFlag Opt_WarnInferredSafeImports)
- l (pkgQual state)
+ inferredImportWarn dflags = singleMessage
+ $ mkMsgEnvelope dflags l (pkgQual state)
+ $ GhcDriverMessage $ DriverUnknownMessage
+ $ mkPlainDiagnostic (WarningWithFlag Opt_WarnInferredSafeImports)
$ sep
[ text "Importing Safe-Inferred module "
<> ppr (moduleName m)
<> text " from explicitly Safe module"
]
- pkgTrustErr = unitBag
- $ mkShortErrorMsgEnvelope l (pkgQual state)
+ pkgTrustErr = singleMessage
+ $ mkErrorMsgEnvelope l (pkgQual state)
+ $ GhcDriverMessage $ DriverUnknownMessage
+ $ mkPlainError
$ sep [ ppr (moduleName m)
<> text ": Can't be safely imported!"
, text "The package ("
<> (pprWithUnitState state $ ppr (moduleUnit m))
<> text ") the module resides in isn't trusted."
]
- modTrustErr = unitBag
- $ mkShortErrorMsgEnvelope l (pkgQual state)
+ modTrustErr = singleMessage
+ $ mkErrorMsgEnvelope l (pkgQual state)
+ $ GhcDriverMessage $ DriverUnknownMessage
+ $ mkPlainError
$ sep [ ppr (moduleName m)
<> text ": Can't be safely imported!"
, text "The module itself isn't safe." ]
@@ -1425,20 +1445,24 @@ hscCheckSafe' m l = do
checkPkgTrust :: Set UnitId -> Hsc ()
checkPkgTrust pkgs = do
hsc_env <- getHscEnv
- let errors = S.foldr go [] pkgs
+ let errors = S.foldr go emptyBag pkgs
state = hsc_units hsc_env
go pkg acc
| unitIsTrusted $ unsafeLookupUnitId state pkg
= acc
| otherwise
- = (:acc) $ mkShortErrorMsgEnvelope noSrcSpan (pkgQual state)
+ = (`consBag` acc)
+ $ mkErrorMsgEnvelope noSrcSpan (pkgQual state)
+ $ GhcDriverMessage
+ $ DriverUnknownMessage
+ $ mkPlainError
$ pprWithUnitState state
$ text "The package ("
<> ppr pkg
<> text ") is required to be trusted but it isn't!"
- case errors of
- [] -> return ()
- _ -> (liftIO . throwIO . mkSrcErr . listToBag) errors
+ if isEmptyBag errors
+ then return ()
+ else liftIO $ throwErrors $ mkMessages errors
-- | Set module to unsafe and (potentially) wipe trust information.
--
@@ -1450,16 +1474,20 @@ checkPkgTrust pkgs = do
-- may call it on modules using Trustworthy or Unsafe flags so as to allow
-- warning flags for safety to function correctly. See Note [Safe Haskell
-- Inference].
-markUnsafeInfer :: TcGblEnv -> WarningMessages -> Hsc TcGblEnv
+markUnsafeInfer :: Diagnostic e => TcGblEnv -> Messages e -> Hsc TcGblEnv
markUnsafeInfer tcg_env whyUnsafe = do
dflags <- getDynFlags
let reason = WarningWithFlag Opt_WarnUnsafe
when (wopt Opt_WarnUnsafe dflags)
- (logDiagnostics $ unitBag $
- mkPlainMsgEnvelope dflags reason (warnUnsafeOnLoc dflags) (whyUnsafe' dflags))
-
- liftIO $ writeIORef (tcg_safeInfer tcg_env) (False, whyUnsafe)
+ (logDiagnostics $ singleMessage $
+ mkPlainMsgEnvelope dflags (warnUnsafeOnLoc dflags) $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic reason $
+ whyUnsafe' dflags)
+
+ liftIO $ writeIORef (tcg_safe_infer tcg_env) False
+ liftIO $ writeIORef (tcg_safe_infer_reasons tcg_env) emptyMessages
-- NOTE: Only wipe trust when not in an explicitly safe haskell mode. Other
-- times inference may be on but we are in Trustworthy mode -- so we want
-- to record safe-inference failed but not wipe the trust dependencies.
@@ -1473,7 +1501,7 @@ markUnsafeInfer tcg_env whyUnsafe = do
whyUnsafe' df = vcat [ quotes pprMod <+> text "has been inferred as unsafe!"
, text "Reason:"
, nest 4 $ (vcat $ badFlags df) $+$
- (vcat $ pprMsgEnvelopeBagWithLoc whyUnsafe) $+$
+ (vcat $ pprMsgEnvelopeBagWithLoc (Error.Types.getMessages whyUnsafe)) $+$
(vcat $ badInsts $ tcg_insts tcg_env)
]
badFlags df = concatMap (badFlag df) unsafeFlagsForInfer
@@ -1689,7 +1717,10 @@ hscCompileCmmFile hsc_env filename output_filename = runHsc hsc_env $ do
$ do
(warns,errs,cmm) <- withTiming logger dflags (text "ParseCmm"<+>brackets (text filename)) (\_ -> ())
$ parseCmmFile dflags cmm_mod home_unit filename
- return (mkMessages (fmap (mkParserWarn dflags) warns `unionBags` fmap mkParserErr errs), cmm)
+ let msgs = foldPsMessages (mkParserWarn dflags) warns
+ `unionMessages`
+ foldPsMessages mkParserErr errs
+ return (msgs, cmm)
liftIO $ do
dumpIfSet_dyn logger dflags Opt_D_dump_cmm_verbose_by_proc "Parsed Cmm" FormatCMM (pdoc platform cmm)
@@ -1889,10 +1920,10 @@ hscParsedStmt :: HscEnv
, FixityEnv))
hscParsedStmt hsc_env stmt = runInteractiveHsc hsc_env $ do
-- Rename and typecheck it
- (ids, tc_expr, fix_env) <- ioMsgMaybe $ tcRnStmt hsc_env stmt
+ (ids, tc_expr, fix_env) <- ioMsgMaybe $ hoistTcRnMessage $ tcRnStmt hsc_env stmt
-- Desugar it
- ds_expr <- ioMsgMaybe $ deSugarExpr hsc_env tc_expr
+ ds_expr <- ioMsgMaybe $ hoistDsMessage $ deSugarExpr hsc_env tc_expr
liftIO (lintInteractiveExpr (text "desugar expression") hsc_env ds_expr)
handleWarnings
@@ -1936,7 +1967,7 @@ hscParsedDecls hsc_env decls = runInteractiveHsc hsc_env $ do
let interp = hscInterp hsc_env
{- Rename and typecheck it -}
- tc_gblenv <- ioMsgMaybe $ tcRnDeclsi hsc_env decls
+ tc_gblenv <- ioMsgMaybe $ hoistTcRnMessage $ tcRnDeclsi hsc_env decls
{- Grab the new instances -}
-- We grab the whole environment because of the overlapping that may have
@@ -2051,6 +2082,7 @@ hscImport hsc_env str = runInteractiveHsc hsc_env $ do
[L _ i] -> return i
_ -> liftIO $ throwOneError $
mkPlainErrorMsgEnvelope noSrcSpan $
+ GhcPsMessage $ PsUnknownMessage $ mkPlainError $
text "parse error in import declaration"
-- | Typecheck an expression (but don't run it)
@@ -2061,7 +2093,7 @@ hscTcExpr :: HscEnv
hscTcExpr hsc_env0 mode expr = runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
parsed_expr <- hscParseExpr expr
- ioMsgMaybe $ tcRnExpr hsc_env mode parsed_expr
+ ioMsgMaybe $ hoistTcRnMessage $ tcRnExpr hsc_env mode parsed_expr
-- | Find the kind of a type, after generalisation
hscKcType
@@ -2072,15 +2104,17 @@ hscKcType
hscKcType hsc_env0 normalise str = runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
ty <- hscParseType str
- ioMsgMaybe $ tcRnType hsc_env DefaultFlexi normalise ty
+ ioMsgMaybe $ hoistTcRnMessage $ tcRnType hsc_env DefaultFlexi normalise ty
hscParseExpr :: String -> Hsc (LHsExpr GhcPs)
hscParseExpr expr = do
maybe_stmt <- hscParseStmt expr
case maybe_stmt of
Just (L _ (BodyStmt _ expr _ _)) -> return expr
- _ -> throwOneError $ mkPlainErrorMsgEnvelope noSrcSpan
- (text "not an expression:" <+> quotes (text expr))
+ _ -> throwOneError $
+ mkPlainErrorMsgEnvelope noSrcSpan $
+ GhcPsMessage $ PsUnknownMessage $ mkPlainError $
+ text "not an expression:" <+> quotes (text expr)
hscParseStmt :: String -> Hsc (Maybe (GhciLStmt GhcPs))
hscParseStmt = hscParseThing parseStmt
diff --git a/compiler/GHC/Driver/Make.hs b/compiler/GHC/Driver/Make.hs
index 484353ae4d..68245b42ca 100644
--- a/compiler/GHC/Driver/Make.hs
+++ b/compiler/GHC/Driver/Make.hs
@@ -61,16 +61,16 @@ import GHC.Driver.Backend
import GHC.Driver.Monad
import GHC.Driver.Env
import GHC.Driver.Errors
+import GHC.Driver.Errors.Types
import GHC.Driver.Main
import GHC.Parser.Header
-import GHC.Parser.Errors.Ppr
import GHC.Iface.Load ( cannotFindModule )
import GHC.IfaceToCore ( typecheckIface )
import GHC.Iface.Recomp ( RecompileRequired ( MustCompile ) )
-import GHC.Data.Bag ( unitBag, listToBag, unionManyBags, isEmptyBag )
+import GHC.Data.Bag ( listToBag )
import GHC.Data.Graph.Directed
import GHC.Data.FastString
import GHC.Data.Maybe ( expectJust )
@@ -164,20 +164,20 @@ depanal :: GhcMonad m =>
-> m ModuleGraph
depanal excluded_mods allow_dup_roots = do
(errs, mod_graph) <- depanalE excluded_mods allow_dup_roots
- if isEmptyBag errs
+ if isEmptyMessages errs
then pure mod_graph
- else throwErrors errs
+ else throwErrors (fmap GhcDriverMessage errs)
-- | Perform dependency analysis like in 'depanal'.
-- In case of errors, the errors and an empty module graph are returned.
depanalE :: GhcMonad m => -- New for #17459
[ModuleName] -- ^ excluded modules
-> Bool -- ^ allow duplicate roots
- -> m (ErrorMessages, ModuleGraph)
+ -> m (DriverMessages, ModuleGraph)
depanalE excluded_mods allow_dup_roots = do
hsc_env <- getSession
(errs, mod_graph) <- depanalPartial excluded_mods allow_dup_roots
- if isEmptyBag errs
+ if isEmptyMessages errs
then do
warnMissingHomeModules hsc_env mod_graph
setSession hsc_env { hsc_mod_graph = mod_graph }
@@ -202,7 +202,7 @@ depanalPartial
:: GhcMonad m
=> [ModuleName] -- ^ excluded modules
-> Bool -- ^ allow duplicate roots
- -> m (ErrorMessages, ModuleGraph)
+ -> m (DriverMessages, ModuleGraph)
-- ^ possibly empty 'Bag' of errors and a module graph.
depanalPartial excluded_mods allow_dup_roots = do
hsc_env <- getSession
@@ -230,7 +230,7 @@ depanalPartial excluded_mods allow_dup_roots = do
(errs, mod_summaries) = partitionEithers mod_summariesE
mod_graph = mkModuleGraph' $
fmap ModuleNode mod_summaries ++ instantiationNodes (hsc_units hsc_env)
- return (unionManyBags errs, mod_graph)
+ return (unionManyMessages errs, mod_graph)
-- | Collect the instantiations of dependencies to create 'InstantiationNode' work graph nodes.
-- These are used to represent the type checking that is done after
@@ -271,7 +271,7 @@ instantiationNodes unit_state = InstantiationNode <$> iuids_to_check
warnMissingHomeModules :: GhcMonad m => HscEnv -> ModuleGraph -> m ()
warnMissingHomeModules hsc_env mod_graph =
when (not (null missing)) $
- logWarnings (listToBag [warn])
+ logDiagnostics warn
where
dflags = hsc_dflags hsc_env
targets = map targetId (hsc_targets hsc_env)
@@ -318,8 +318,10 @@ warnMissingHomeModules hsc_env mod_graph =
(text "Modules are not listed in command line but needed for compilation: ")
4
(sep (map ppr missing))
- warn =
- mkPlainMsgEnvelope (hsc_dflags hsc_env) (WarningWithFlag Opt_WarnMissingHomeModules) noSrcSpan msg
+ warn = singleMessage $
+ mkPlainMsgEnvelope (hsc_dflags hsc_env) noSrcSpan $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic (WarningWithFlag Opt_WarnMissingHomeModules) msg
-- | Describes which modules of the module graph need to be loaded.
data LoadHowMuch
@@ -351,9 +353,9 @@ load how_much = do
(errs, mod_graph) <- depanalE [] False -- #17459
success <- load' how_much (Just batchMsg) mod_graph
warnUnusedPackages
- if isEmptyBag errs
+ if isEmptyMessages errs
then pure success
- else throwErrors errs
+ else throwErrors (fmap GhcDriverMessage errs)
-- Note [Unused packages]
--
@@ -384,15 +386,17 @@ warnUnusedPackages = do
= filter (\arg -> not $ any (matching state arg) loadedPackages)
requestedArgs
- let warn =
- mkPlainMsgEnvelope dflags (WarningWithFlag Opt_WarnUnusedPackages) noSrcSpan msg
+ let warn = singleMessage $
+ mkPlainMsgEnvelope dflags noSrcSpan $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic (WarningWithFlag Opt_WarnUnusedPackages) msg
msg = vcat [ text "The following packages were specified" <+>
text "via -package or -package-id flags,"
, text "but were not needed for compilation:"
, nest 2 (vcat (map (withDash . pprUnusedArg) unusedArgs)) ]
when (not (null unusedArgs)) $
- logWarnings (listToBag [warn])
+ logDiagnostics warn
where
packageArg (ExposePackage _ arg _) = Just arg
@@ -1419,7 +1423,7 @@ parUpsweep_one mod home_mod_map comp_graph_loops lcl_logger lcl_tmpfs lcl_dflags
hsc_env <- readMVar hsc_env_var
old_hpt <- readIORef old_hpt_var
- let logg err = printBagOfErrors lcl_logger lcl_dflags (srcErrorMessages err)
+ let logg err = printMessages lcl_logger lcl_dflags (srcErrorMessages err)
-- Limit the number of parallel compiles.
let withSem sem = MC.bracket_ (waitQSem sem) (signalQSem sem)
@@ -1671,7 +1675,7 @@ upsweep_inst hsc_env mHscMessage mod_index nmods iuid = do
case mHscMessage of
Just hscMessage -> hscMessage hsc_env (mod_index, nmods) MustCompile (InstantiationNode iuid)
Nothing -> return ()
- runHsc hsc_env $ ioMsgMaybe $ tcRnCheckUnit hsc_env $ VirtUnit iuid
+ runHsc hsc_env $ ioMsgMaybe $ hoistTcRnMessage $ tcRnCheckUnit hsc_env $ VirtUnit iuid
pure ()
-- | Compile a single module. Always produce a Linkable for it if
@@ -2214,17 +2218,18 @@ warnUnnecessarySourceImports :: GhcMonad m => [SCC ModSummary] -> m ()
warnUnnecessarySourceImports sccs = do
dflags <- getDynFlags
when (wopt Opt_WarnUnusedImports dflags)
- (logWarnings (listToBag (concatMap (check dflags . flattenSCC) sccs)))
+ (logDiagnostics (mkMessages $ listToBag (concatMap (check dflags . flattenSCC) sccs)))
where check dflags ms =
let mods_in_this_cycle = map ms_mod_name ms in
[ warn dflags i | m <- ms, i <- ms_home_srcimps m,
unLoc i `notElem` mods_in_this_cycle ]
- warn :: DynFlags -> Located ModuleName -> WarnMsg
+ warn :: DynFlags -> Located ModuleName -> MsgEnvelope GhcMessage
warn dflags (L loc mod) =
- mkPlainMsgEnvelope dflags WarningWithoutFlag loc
- (text "{-# SOURCE #-} unnecessary in import of "
- <+> quotes (ppr mod))
+ mkPlainMsgEnvelope dflags loc $
+ GhcDriverMessage $ DriverUnknownMessage $
+ mkPlainDiagnostic WarningWithoutFlag $
+ text "{-# SOURCE #-} unnecessary in import of " <+> quotes (ppr mod)
-----------------------------------------------------------------------------
@@ -2250,7 +2255,7 @@ downsweep :: HscEnv
-> Bool -- True <=> allow multiple targets to have
-- the same module name; this is
-- very useful for ghc -M
- -> IO [Either ErrorMessages ExtendedModSummary]
+ -> IO [Either DriverMessages ExtendedModSummary]
-- The non-error elements of the returned list all have distinct
-- (Modules, IsBoot) identifiers, unless the Bool is true in
-- which case there can be repeats
@@ -2286,7 +2291,7 @@ downsweep hsc_env old_summaries excl_mods allow_dup_roots
old_summary_map :: ModNodeMap ExtendedModSummary
old_summary_map = mkNodeMap old_summaries
- getRootSummary :: Target -> IO (Either ErrorMessages ExtendedModSummary)
+ getRootSummary :: Target -> IO (Either DriverMessages ExtendedModSummary)
getRootSummary Target { targetId = TargetFile file mb_phase
, targetAllowObjCode = obj_allowed
, targetContents = maybe_buf
@@ -2295,8 +2300,10 @@ downsweep hsc_env old_summaries excl_mods allow_dup_roots
if exists || isJust maybe_buf
then summariseFile hsc_env old_summaries file mb_phase
obj_allowed maybe_buf
- else return $ Left $ unitBag $ mkPlainErrorMsgEnvelope noSrcSpan $
- text "can't find file:" <+> text file
+ else return $ Left $ singleMessage $
+ mkPlainErrorMsgEnvelope noSrcSpan $
+ DriverUnknownMessage $ mkPlainError $
+ text "can't find file:" <+> text file
getRootSummary Target { targetId = TargetModule modl
, targetAllowObjCode = obj_allowed
, targetContents = maybe_buf
@@ -2316,7 +2323,7 @@ downsweep hsc_env old_summaries excl_mods allow_dup_roots
-- ignored, leading to confusing behaviour).
checkDuplicates
:: ModNodeMap
- [Either ErrorMessages
+ [Either DriverMessages
ExtendedModSummary]
-> IO ()
checkDuplicates root_map
@@ -2329,11 +2336,11 @@ downsweep hsc_env old_summaries excl_mods allow_dup_roots
loop :: [GenWithIsBoot (Located ModuleName)]
-- Work list: process these modules
- -> ModNodeMap [Either ErrorMessages ExtendedModSummary]
+ -> ModNodeMap [Either DriverMessages ExtendedModSummary]
-- Visited set; the range is a list because
-- the roots can have the same module names
-- if allow_dup_roots is True
- -> IO (ModNodeMap [Either ErrorMessages ExtendedModSummary])
+ -> IO (ModNodeMap [Either DriverMessages ExtendedModSummary])
-- The result is the completed NodeMap
loop [] done = return done
loop (s : ss) done
@@ -2373,8 +2380,8 @@ enableCodeGenForTH
-> TmpFs
-> HomeUnit
-> Backend
- -> ModNodeMap [Either ErrorMessages ExtendedModSummary]
- -> IO (ModNodeMap [Either ErrorMessages ExtendedModSummary])
+ -> ModNodeMap [Either DriverMessages ExtendedModSummary]
+ -> IO (ModNodeMap [Either DriverMessages ExtendedModSummary])
enableCodeGenForTH logger tmpfs home_unit =
enableCodeGenWhen logger tmpfs condition should_modify TFL_CurrentModule TFL_GhcSession
where
@@ -2399,8 +2406,8 @@ enableCodeGenWhen
-> TempFileLifetime
-> TempFileLifetime
-> Backend
- -> ModNodeMap [Either ErrorMessages ExtendedModSummary]
- -> IO (ModNodeMap [Either ErrorMessages ExtendedModSummary])
+ -> ModNodeMap [Either DriverMessages ExtendedModSummary]
+ -> IO (ModNodeMap [Either DriverMessages ExtendedModSummary])
enableCodeGenWhen logger tmpfs condition should_modify staticLife dynLife bcknd nodemap =
traverse (traverse (traverse enable_code_gen)) nodemap
where
@@ -2469,7 +2476,7 @@ enableCodeGenWhen logger tmpfs condition should_modify staticLife dynLife bcknd
mkRootMap
:: [ExtendedModSummary]
- -> ModNodeMap [Either ErrorMessages ExtendedModSummary]
+ -> ModNodeMap [Either DriverMessages ExtendedModSummary]
mkRootMap summaries = ModNodeMap $ Map.insertListWith
(flip (++))
[ (msKey $ emsModSummary s, [Right s]) | s <- summaries ]
@@ -2514,7 +2521,7 @@ summariseFile
-> Maybe Phase -- start phase
-> Bool -- object code allowed?
-> Maybe (StringBuffer,UTCTime)
- -> IO (Either ErrorMessages ExtendedModSummary)
+ -> IO (Either DriverMessages ExtendedModSummary)
summariseFile hsc_env old_summaries src_fn mb_phase obj_allowed maybe_buf
-- we can use a cached summary if one is available and the
@@ -2642,7 +2649,7 @@ summariseModule
-> Bool -- object code allowed?
-> Maybe (StringBuffer, UTCTime)
-> [ModuleName] -- Modules to exclude
- -> IO (Maybe (Either ErrorMessages ExtendedModSummary)) -- Its new summary
+ -> IO (Maybe (Either DriverMessages ExtendedModSummary)) -- Its new summary
summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod)
obj_allowed maybe_buf excl_mods
@@ -2730,7 +2737,9 @@ summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod)
| otherwise = HsSrcFile
when (pi_mod_name /= wanted_mod) $
- throwE $ unitBag $ mkPlainErrorMsgEnvelope pi_mod_name_loc $
+ throwE $ singleMessage $
+ mkPlainErrorMsgEnvelope pi_mod_name_loc $
+ DriverUnknownMessage $ mkPlainError $
text "File name does not match module name:"
$$ text "Saw:" <+> quotes (ppr pi_mod_name)
$$ text "Expected:" <+> quotes (ppr wanted_mod)
@@ -2742,7 +2751,9 @@ summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod)
| (k,v) <- ((pi_mod_name, mkHoleModule pi_mod_name)
: homeUnitInstantiations home_unit)
])
- in throwE $ unitBag $ mkPlainErrorMsgEnvelope pi_mod_name_loc $
+ in throwE $ singleMessage $
+ mkPlainErrorMsgEnvelope pi_mod_name_loc $
+ DriverUnknownMessage $ mkPlainError $
text "Unexpected signature:" <+> quotes (ppr pi_mod_name)
$$ if gopt Opt_BuildingCabalPackage dflags
then parens (text "Try adding" <+> quotes (ppr pi_mod_name)
@@ -2845,7 +2856,7 @@ getPreprocessedImports
-> Maybe Phase
-> Maybe (StringBuffer, UTCTime)
-- ^ optional source code buffer and modification time
- -> ExceptT ErrorMessages IO PreprocessedImports
+ -> ExceptT DriverMessages IO PreprocessedImports
getPreprocessedImports hsc_env src_fn mb_phase maybe_buf = do
(pi_local_dflags, pi_hspp_fn)
<- ExceptT $ preprocess hsc_env src_fn (fst <$> maybe_buf) mb_phase
@@ -2855,7 +2866,7 @@ getPreprocessedImports hsc_env src_fn mb_phase maybe_buf = do
let imp_prelude = xopt LangExt.ImplicitPrelude pi_local_dflags
popts = initParserOpts pi_local_dflags
mimps <- getImports popts imp_prelude pi_hspp_buf pi_hspp_fn src_fn
- return (first (fmap mkParserErr) mimps)
+ return (first (mkMessages . fmap mkDriverPsHeaderMessage) mimps)
return PreprocessedImports {..}
@@ -2899,24 +2910,30 @@ withDeferredDiagnostics f = do
(\_ -> popLogHookM >> printDeferredDiagnostics)
(\_ -> f)
-noModError :: HscEnv -> SrcSpan -> ModuleName -> FindResult -> MsgEnvelope DiagnosticMessage
+noModError :: HscEnv -> SrcSpan -> ModuleName -> FindResult -> MsgEnvelope GhcMessage
-- ToDo: we don't have a proper line number for this error
noModError hsc_env loc wanted_mod err
- = mkPlainErrorMsgEnvelope loc $ cannotFindModule hsc_env wanted_mod err
+ = mkPlainErrorMsgEnvelope loc $ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
+ cannotFindModule hsc_env wanted_mod err
-noHsFileErr :: SrcSpan -> String -> ErrorMessages
+noHsFileErr :: SrcSpan -> String -> DriverMessages
noHsFileErr loc path
- = unitBag $ mkPlainErrorMsgEnvelope loc $ text "Can't find" <+> text path
+ = singleMessage $ mkPlainErrorMsgEnvelope loc $
+ DriverUnknownMessage $ mkPlainError $
+ text "Can't find" <+> text path
-moduleNotFoundErr :: ModuleName -> ErrorMessages
+moduleNotFoundErr :: ModuleName -> DriverMessages
moduleNotFoundErr mod
- = unitBag $ mkPlainErrorMsgEnvelope noSrcSpan $
+ = singleMessage $ mkPlainErrorMsgEnvelope noSrcSpan $
+ DriverUnknownMessage $ mkPlainError $
text "module" <+> quotes (ppr mod) <+> text "cannot be found locally"
multiRootsErr :: [ModSummary] -> IO ()
multiRootsErr [] = panic "multiRootsErr"
multiRootsErr summs@(summ1:_)
- = throwOneError $ mkPlainErrorMsgEnvelope noSrcSpan $
+ = throwOneError $
+ mkPlainErrorMsgEnvelope noSrcSpan $
+ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
text "module" <+> quotes (ppr mod) <+>
text "is defined in multiple files:" <+>
sep (map text files)
diff --git a/compiler/GHC/Driver/MakeFile.hs b/compiler/GHC/Driver/MakeFile.hs
index ea1bf1f501..6acc547202 100644
--- a/compiler/GHC/Driver/MakeFile.hs
+++ b/compiler/GHC/Driver/MakeFile.hs
@@ -23,6 +23,7 @@ import GHC.Driver.Session
import GHC.Driver.Ppr
import GHC.Utils.Misc
import GHC.Driver.Env
+import GHC.Driver.Errors.Types
import qualified GHC.SysTools as SysTools
import GHC.Data.Graph.Directed ( SCC(..) )
import GHC.Utils.Outputable
@@ -305,7 +306,9 @@ findDependency hsc_env srcloc pkg imp is_boot include_pkg_deps = do
-> return Nothing
fail ->
- throwOneError $ mkPlainErrorMsgEnvelope srcloc $
+ throwOneError $
+ mkPlainErrorMsgEnvelope srcloc $
+ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
cannotFindModule hsc_env imp fail
-----------------------------
@@ -454,4 +457,3 @@ pprCycle summaries = pp_group (CyclicSCC summaries)
depStartMarker, depEndMarker :: String
depStartMarker = "# DO NOT DELETE: Beginning of Haskell dependencies"
depEndMarker = "# DO NOT DELETE: End of Haskell dependencies"
-
diff --git a/compiler/GHC/Driver/Monad.hs b/compiler/GHC/Driver/Monad.hs
index 1a42d8402f..2fa3c51cc1 100644
--- a/compiler/GHC/Driver/Monad.hs
+++ b/compiler/GHC/Driver/Monad.hs
@@ -27,8 +27,8 @@ module GHC.Driver.Monad (
putMsgM,
withTimingM,
- -- ** Warnings
- logWarnings, printException,
+ -- ** Diagnostics
+ logDiagnostics, printException,
WarnErrLogger, defaultWarnErrLogger
) where
@@ -36,7 +36,8 @@ import GHC.Prelude
import GHC.Driver.Session
import GHC.Driver.Env
-import GHC.Driver.Errors ( printOrThrowDiagnostics, printBagOfErrors )
+import GHC.Driver.Errors ( printOrThrowDiagnostics, printMessages )
+import GHC.Driver.Errors.Types
import GHC.Utils.Monad
import GHC.Utils.Exception
@@ -141,10 +142,10 @@ withTimingM doc force action = do
withTiming logger dflags doc force action
-- -----------------------------------------------------------------------------
--- | A monad that allows logging of warnings.
+-- | A monad that allows logging of diagnostics.
-logWarnings :: GhcMonad m => WarningMessages -> m ()
-logWarnings warns = do
+logDiagnostics :: GhcMonad m => Messages GhcMessage -> m ()
+logDiagnostics warns = do
dflags <- getSessionDynFlags
logger <- getLogger
liftIO $ printOrThrowDiagnostics logger dflags warns
@@ -240,13 +241,13 @@ instance ExceptionMonad m => GhcMonad (GhcT m) where
setSession s' = GhcT $ \(Session r) -> liftIO $ writeIORef r s'
--- | Print the error message and all warnings. Useful inside exception
--- handlers. Clears warnings after printing.
+-- | Print the all diagnostics in a 'SourceError'. Useful inside exception
+-- handlers.
printException :: GhcMonad m => SourceError -> m ()
printException err = do
dflags <- getSessionDynFlags
logger <- getLogger
- liftIO $ printBagOfErrors logger dflags (srcErrorMessages err)
+ liftIO $ printMessages logger dflags (srcErrorMessages err)
-- | A function called to log warnings and errors.
type WarnErrLogger = forall m. GhcMonad m => Maybe SourceError -> m ()
@@ -254,4 +255,3 @@ type WarnErrLogger = forall m. GhcMonad m => Maybe SourceError -> m ()
defaultWarnErrLogger :: WarnErrLogger
defaultWarnErrLogger Nothing = return ()
defaultWarnErrLogger (Just e) = printException e
-
diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs
index 8589b81ee5..5496fe31a2 100644
--- a/compiler/GHC/Driver/Pipeline.hs
+++ b/compiler/GHC/Driver/Pipeline.hs
@@ -47,6 +47,7 @@ import GHC.Tc.Types
import GHC.Driver.Main
import GHC.Driver.Env hiding ( Hsc )
import GHC.Driver.Errors
+import GHC.Driver.Errors.Types
import GHC.Driver.Pipeline.Monad
import GHC.Driver.Config
import GHC.Driver.Phases
@@ -81,7 +82,6 @@ import GHC.CmmToLlvm ( llvmFixupAsm, llvmVersionList )
import qualified GHC.LanguageExtensions as LangExt
import GHC.Settings
-import GHC.Data.Bag ( unitBag )
import GHC.Data.FastString ( mkFastString )
import GHC.Data.StringBuffer ( hGetStringBuffer, hPutStringBuffer )
import GHC.Data.Maybe ( expectJust )
@@ -89,6 +89,7 @@ import GHC.Data.Maybe ( expectJust )
import GHC.Iface.Make ( mkFullIface )
import GHC.Types.Basic ( SuccessFlag(..) )
+import GHC.Types.Error ( singleMessage, getMessages )
import GHC.Types.Target
import GHC.Types.SrcLoc
import GHC.Types.SourceFile
@@ -130,9 +131,9 @@ preprocess :: HscEnv
-> Maybe InputFileBuffer
-- ^ optional buffer to use instead of reading the input file
-> Maybe Phase -- ^ starting phase
- -> IO (Either ErrorMessages (DynFlags, FilePath))
+ -> IO (Either DriverMessages (DynFlags, FilePath))
preprocess hsc_env input_fn mb_input_buf mb_phase =
- handleSourceError (\err -> return (Left (srcErrorMessages err))) $
+ handleSourceError (\err -> return $ Left $ to_driver_messages $ srcErrorMessages err) $
MC.handle handler $
fmap Right $ do
MASSERT2(isJust mb_phase || isHaskellSrcFilename input_fn, text input_fn)
@@ -148,10 +149,21 @@ preprocess hsc_env input_fn mb_input_buf mb_phase =
return (dflags, fp)
where
srcspan = srcLocSpan $ mkSrcLoc (mkFastString input_fn) 1 1
- handler (ProgramError msg) = return $ Left $ unitBag $
- mkPlainErrorMsgEnvelope srcspan $ text msg
+ handler (ProgramError msg) =
+ return $ Left $ singleMessage $
+ mkPlainErrorMsgEnvelope srcspan $
+ DriverUnknownMessage $ mkPlainError $ text msg
handler ex = throwGhcExceptionIO ex
+ to_driver_messages :: Messages GhcMessage -> Messages DriverMessage
+ to_driver_messages msgs = case traverse to_driver_message msgs of
+ Nothing -> pprPanic "non-driver message in preprocess"
+ (vcat $ pprMsgEnvelopeBagWithLoc (getMessages msgs))
+ Just msgs' -> msgs'
+
+ to_driver_message (GhcDriverMessage msg) = Just msg
+ to_driver_message _other = Nothing
+
-- ---------------------------------------------------------------------------
-- | Compile
@@ -1259,7 +1271,7 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn
popts = initParserOpts dflags
eimps <- getImports popts imp_prelude buf input_fn (basename <.> suff)
case eimps of
- Left errs -> throwErrors (fmap mkParserErr errs)
+ Left errs -> throwErrors (foldPsMessages mkParserErr errs)
Right (src_imps,imps,L _ mod_name) -> return
(Just buf, mod_name, imps, src_imps)
diff --git a/compiler/GHC/HsToCore.hs b/compiler/GHC/HsToCore.hs
index a4bbc290e2..a5f638ab12 100644
--- a/compiler/GHC/HsToCore.hs
+++ b/compiler/GHC/HsToCore.hs
@@ -29,6 +29,7 @@ import GHC.Hs
import GHC.HsToCore.Usage
import GHC.HsToCore.Monad
+import GHC.HsToCore.Errors.Types
import GHC.HsToCore.Expr
import GHC.HsToCore.Binds
import GHC.HsToCore.Foreign.Decl
@@ -59,6 +60,7 @@ import GHC.Builtin.Types.Prim
import GHC.Builtin.Types
import GHC.Data.FastString
+import GHC.Data.Maybe ( expectJust )
import GHC.Data.OrdList
import GHC.Utils.Error
@@ -82,7 +84,6 @@ import GHC.Types.Name.Set
import GHC.Types.Name.Env
import GHC.Types.Name.Ppr
import GHC.Types.HpcInfo
-import GHC.Types.Error
import GHC.Unit
import GHC.Unit.Module.ModGuts
@@ -101,7 +102,7 @@ import GHC.Driver.Plugins ( LoadedPlugin(..) )
-}
-- | Main entry point to the desugarer.
-deSugar :: HscEnv -> ModLocation -> TcGblEnv -> IO (Messages DiagnosticMessage, Maybe ModGuts)
+deSugar :: HscEnv -> ModLocation -> TcGblEnv -> IO (Messages DsMessage, Maybe ModGuts)
-- Can modify PCS by faulting in more declarations
deSugar hsc_env
@@ -285,7 +286,7 @@ So we pull out the type/coercion variables (which are in dependency order),
and Rec the rest.
-}
-deSugarExpr :: HscEnv -> LHsExpr GhcTc -> IO (Messages DiagnosticMessage, Maybe CoreExpr)
+deSugarExpr :: HscEnv -> LHsExpr GhcTc -> IO (Messages DsMessage, Maybe CoreExpr)
deSugarExpr hsc_env tc_expr = do
let dflags = hsc_dflags hsc_env
let logger = hsc_logger hsc_env
@@ -293,15 +294,27 @@ deSugarExpr hsc_env tc_expr = do
showPass logger dflags "Desugar"
-- Do desugaring
- (msgs, mb_core_expr) <- runTcInteractive hsc_env $ initDsTc $
- dsLExpr tc_expr
+ (tc_msgs, mb_result) <- runTcInteractive hsc_env $
+ initDsTc $
+ dsLExpr tc_expr
+
+ MASSERT( isEmptyMessages tc_msgs ) -- the type-checker isn't doing anything here
+
+ -- mb_result is Nothing only when a failure happens in the type-checker,
+ -- but mb_core_expr is Nothing when a failure happens in the desugarer
+ let (ds_msgs, mb_core_expr) = expectJust "deSugarExpr" mb_result
case mb_core_expr of
Nothing -> return ()
Just expr -> dumpIfSet_dyn logger dflags Opt_D_dump_ds "Desugared"
FormatCore (pprCoreExpr expr)
- return (msgs, mb_core_expr)
+ -- callers (i.e. ioMsgMaybe) expect that no expression is returned if
+ -- there are errors
+ let final_res | errorsFound ds_msgs = Nothing
+ | otherwise = mb_core_expr
+
+ return (ds_msgs, final_res)
{-
************************************************************************
diff --git a/compiler/GHC/HsToCore/Errors/Ppr.hs b/compiler/GHC/HsToCore/Errors/Ppr.hs
new file mode 100644
index 0000000000..f453a82743
--- /dev/null
+++ b/compiler/GHC/HsToCore/Errors/Ppr.hs
@@ -0,0 +1,11 @@
+
+{-# OPTIONS_GHC -fno-warn-orphans #-} -- instance Diagnostic DsMessage
+
+module GHC.HsToCore.Errors.Ppr where
+
+import GHC.Types.Error
+import GHC.HsToCore.Errors.Types
+
+instance Diagnostic DsMessage where
+ diagnosticMessage (DsUnknownMessage m) = diagnosticMessage m
+ diagnosticReason (DsUnknownMessage m) = diagnosticReason m
diff --git a/compiler/GHC/HsToCore/Errors/Types.hs b/compiler/GHC/HsToCore/Errors/Types.hs
new file mode 100644
index 0000000000..45a47d5c30
--- /dev/null
+++ b/compiler/GHC/HsToCore/Errors/Types.hs
@@ -0,0 +1,10 @@
+
+module GHC.HsToCore.Errors.Types where
+
+import GHC.Types.Error
+
+-- | Diagnostics messages emitted during desugaring.
+data DsMessage =
+ DsUnknownMessage !DiagnosticMessage
+ -- ^ Simply rewraps a generic 'DiagnosticMessage'. More
+ -- constructors will be added in the future (#18516).
diff --git a/compiler/GHC/HsToCore/Monad.hs b/compiler/GHC/HsToCore/Monad.hs
index 788f4828e2..9bc893f814 100644
--- a/compiler/GHC/HsToCore/Monad.hs
+++ b/compiler/GHC/HsToCore/Monad.hs
@@ -64,6 +64,7 @@ import GHC.Driver.Ppr
import GHC.Hs
import GHC.HsToCore.Types
+import GHC.HsToCore.Errors.Types
import GHC.HsToCore.Pmc.Solver.Types (Nablas, initNablas)
import GHC.Core.FamInstEnv
@@ -204,17 +205,22 @@ type DsWarning = (SrcSpan, SDoc)
-- into a Doc.
-- | Run a 'DsM' action inside the 'TcM' monad.
-initDsTc :: DsM a -> TcM a
+initDsTc :: DsM a -> TcM (Messages DsMessage, Maybe a)
initDsTc thing_inside
= do { tcg_env <- getGblEnv
- ; msg_var <- getErrsVar
+ ; msg_var <- liftIO $ newIORef emptyMessages
; hsc_env <- getTopEnv
; envs <- mkDsEnvsFromTcGbl hsc_env msg_var tcg_env
- ; setEnvs envs thing_inside
+ ; e_result <- tryM $ -- need to tryM so that we don't discard
+ -- DsMessages
+ setEnvs envs thing_inside
+ ; msgs <- liftIO $ readIORef msg_var
+ ; return (msgs, case e_result of Left _ -> Nothing
+ Right x -> Just x)
}
-- | Run a 'DsM' action inside the 'IO' monad.
-initDs :: HscEnv -> TcGblEnv -> DsM a -> IO (Messages DiagnosticMessage, Maybe a)
+initDs :: HscEnv -> TcGblEnv -> DsM a -> IO (Messages DsMessage, Maybe a)
initDs hsc_env tcg_env thing_inside
= do { msg_var <- newIORef emptyMessages
; envs <- mkDsEnvsFromTcGbl hsc_env msg_var tcg_env
@@ -223,7 +229,7 @@ initDs hsc_env tcg_env thing_inside
-- | Build a set of desugarer environments derived from a 'TcGblEnv'.
mkDsEnvsFromTcGbl :: MonadIO m
- => HscEnv -> IORef (Messages DiagnosticMessage) -> TcGblEnv
+ => HscEnv -> IORef (Messages DsMessage) -> TcGblEnv
-> m (DsGblEnv, DsLclEnv)
mkDsEnvsFromTcGbl hsc_env msg_var tcg_env
= do { cc_st_var <- liftIO $ newIORef newCostCentreState
@@ -242,7 +248,7 @@ mkDsEnvsFromTcGbl hsc_env msg_var tcg_env
msg_var cc_st_var next_wrapper_num_var complete_matches
}
-runDs :: HscEnv -> (DsGblEnv, DsLclEnv) -> DsM a -> IO (Messages DiagnosticMessage, Maybe a)
+runDs :: HscEnv -> (DsGblEnv, DsLclEnv) -> DsM a -> IO (Messages DsMessage, Maybe a)
runDs hsc_env (ds_gbl, ds_lcl) thing_inside
= do { res <- initTcRnIf 'd' hsc_env ds_gbl ds_lcl
(tryM thing_inside)
@@ -255,7 +261,7 @@ runDs hsc_env (ds_gbl, ds_lcl) thing_inside
}
-- | Run a 'DsM' action in the context of an existing 'ModGuts'
-initDsWithModGuts :: HscEnv -> ModGuts -> DsM a -> IO (Messages DiagnosticMessage, Maybe a)
+initDsWithModGuts :: HscEnv -> ModGuts -> DsM a -> IO (Messages DsMessage, Maybe a)
initDsWithModGuts hsc_env (ModGuts { mg_module = this_mod, mg_binds = binds
, mg_tcs = tycons, mg_fam_insts = fam_insts
, mg_patsyns = patsyns, mg_rdr_env = rdr_env
@@ -316,7 +322,7 @@ initTcDsForSolver thing_inside
Nothing -> pprPanic "initTcDsForSolver" (vcat $ pprMsgEnvelopeBagWithLoc (getErrorMessages msgs)) }
mkDsEnvs :: UnitEnv -> Module -> GlobalRdrEnv -> TypeEnv -> FamInstEnv
- -> IORef (Messages DiagnosticMessage) -> IORef CostCentreState
+ -> IORef (Messages DsMessage) -> IORef CostCentreState
-> IORef (ModuleEnv Int) -> CompleteMatches
-> (DsGblEnv, DsLclEnv)
mkDsEnvs unit_env mod rdr_env type_env fam_inst_env msg_var cc_st_var
@@ -466,7 +472,9 @@ diagnosticDs reason warn
= do { env <- getGblEnv
; loc <- getSrcSpanDs
; dflags <- getDynFlags
- ; let msg = mkShortMsgEnvelope dflags reason loc (ds_unqual env) warn
+ ; let msg = mkMsgEnvelope dflags loc (ds_unqual env) $
+ DsUnknownMessage $
+ mkPlainDiagnostic reason warn
; updMutVar (ds_msgs env) (\ msgs -> msg `addMessage` msgs) }
-- | Emit a warning only if the correct WarningWithoutFlag is set in the DynFlags
@@ -479,7 +487,9 @@ errDs :: SDoc -> DsM ()
errDs err
= do { env <- getGblEnv
; loc <- getSrcSpanDs
- ; let msg = mkShortErrorMsgEnvelope loc (ds_unqual env) err
+ ; let msg = mkErrorMsgEnvelope loc (ds_unqual env) $
+ DsUnknownMessage $
+ mkPlainError err
; updMutVar (ds_msgs env) (\ msgs -> msg `addMessage` msgs) }
-- | Issue an error, but return the expression for (), so that we can continue
diff --git a/compiler/GHC/HsToCore/Types.hs b/compiler/GHC/HsToCore/Types.hs
index 58273e250e..bc9d7b4c1d 100644
--- a/compiler/GHC/HsToCore/Types.hs
+++ b/compiler/GHC/HsToCore/Types.hs
@@ -19,6 +19,7 @@ import GHC.Types.Name.Reader (GlobalRdrEnv)
import GHC.Hs (LForeignDecl, HsExpr, GhcTc)
import GHC.Tc.Types (TcRnIf, IfGblEnv, IfLclEnv, CompleteMatches)
import GHC.HsToCore.Pmc.Types (Nablas)
+import GHC.HsToCore.Errors.Types
import GHC.Core (CoreExpr)
import GHC.Core.FamInstEnv
import GHC.Utils.Outputable as Outputable
@@ -49,7 +50,7 @@ data DsGblEnv
-- constructors are in scope during
-- pattern-match satisfiability checking
, ds_unqual :: PrintUnqualified
- , ds_msgs :: IORef (Messages DiagnosticMessage) -- Diagnostic messages
+ , ds_msgs :: IORef (Messages DsMessage) -- Diagnostic messages
, ds_if_env :: (IfGblEnv, IfLclEnv) -- Used for looking up global,
-- possibly-imported things
, ds_complete_matches :: CompleteMatches
diff --git a/compiler/GHC/Iface/Load.hs b/compiler/GHC/Iface/Load.hs
index b52498129f..9c3417825b 100644
--- a/compiler/GHC/Iface/Load.hs
+++ b/compiler/GHC/Iface/Load.hs
@@ -44,6 +44,7 @@ import {-# SOURCE #-} GHC.IfaceToCore
, tcIfaceAnnotations, tcIfaceCompleteMatches )
import GHC.Driver.Env
+import GHC.Driver.Errors.Types
import GHC.Driver.Session
import GHC.Driver.Backend
import GHC.Driver.Ppr
@@ -707,7 +708,7 @@ computeInterface hsc_env doc_str hi_boot_file mod0 = do
Succeeded (iface0, path) ->
rnModIface hsc_env (instUnitInsts (moduleUnit indef)) Nothing iface0 >>= \case
Right x -> return (Succeeded (x, path))
- Left errs -> throwErrors errs
+ Left errs -> throwErrors (GhcTcRnMessage <$> errs)
Failed err -> return (Failed err)
(mod, _) -> find_iface mod
@@ -1224,4 +1225,3 @@ pprExtensibleFields :: ExtensibleFields -> SDoc
pprExtensibleFields (ExtensibleFields fs) = vcat . map pprField $ toList fs
where
pprField (name, (BinData size _data)) = text name <+> text "-" <+> ppr size <+> text "bytes"
-
diff --git a/compiler/GHC/Iface/Rename.hs b/compiler/GHC/Iface/Rename.hs
index f4e8b449f5..500e12a1db 100644
--- a/compiler/GHC/Iface/Rename.hs
+++ b/compiler/GHC/Iface/Rename.hs
@@ -31,6 +31,7 @@ import GHC.Unit.State
import GHC.Unit.Module.ModIface
import GHC.Unit.Module.Deps
+import GHC.Tc.Errors.Types
import GHC.Types.SrcLoc
import GHC.Types.Unique.FM
import GHC.Types.Avail
@@ -47,18 +48,16 @@ import GHC.Utils.Error
import GHC.Utils.Fingerprint
import GHC.Utils.Panic
-import GHC.Data.Bag
-
import qualified Data.Traversable as T
import Data.IORef
-tcRnMsgMaybe :: IO (Either ErrorMessages a) -> TcM a
+tcRnMsgMaybe :: IO (Either (Messages TcRnMessage) a) -> TcM a
tcRnMsgMaybe do_this = do
r <- liftIO $ do_this
case r of
- Left errs -> do
- addMessages (mkMessages errs)
+ Left msgs -> do
+ addMessages msgs
failM
Right x -> return x
@@ -77,7 +76,10 @@ failWithRn doc = do
errs_var <- fmap sh_if_errs getGblEnv
errs <- readTcRef errs_var
-- TODO: maybe associate this with a source location?
- writeTcRef errs_var (errs `snocBag` mkPlainErrorMsgEnvelope noSrcSpan doc)
+ let msg = mkPlainErrorMsgEnvelope noSrcSpan $
+ TcRnUnknownMessage $
+ mkPlainError doc
+ writeTcRef errs_var (msg `addMessage` errs)
failM
-- | What we have is a generalized ModIface, which corresponds to
@@ -101,7 +103,7 @@ failWithRn doc = do
-- should be Foo.T; then we'll also rename this (this is used
-- when loading an interface to merge it into a requirement.)
rnModIface :: HscEnv -> [(ModuleName, Module)] -> Maybe NameShape
- -> ModIface -> IO (Either ErrorMessages ModIface)
+ -> ModIface -> IO (Either (Messages TcRnMessage) ModIface)
rnModIface hsc_env insts nsubst iface =
initRnIface hsc_env iface insts nsubst $ do
mod <- rnModule (mi_module iface)
@@ -125,7 +127,7 @@ rnModIface hsc_env insts nsubst iface =
-- | Rename just the exports of a 'ModIface'. Useful when we're doing
-- shaping prior to signature merging.
-rnModExports :: HscEnv -> [(ModuleName, Module)] -> ModIface -> IO (Either ErrorMessages [AvailInfo])
+rnModExports :: HscEnv -> [(ModuleName, Module)] -> ModIface -> IO (Either (Messages TcRnMessage) [AvailInfo])
rnModExports hsc_env insts iface
= initRnIface hsc_env iface insts Nothing
$ mapM rnAvailInfo (mi_exports iface)
@@ -185,9 +187,9 @@ rnDepModules sel deps = do
-- | Run a computation in the 'ShIfM' monad.
initRnIface :: HscEnv -> ModIface -> [(ModuleName, Module)] -> Maybe NameShape
- -> ShIfM a -> IO (Either ErrorMessages a)
+ -> ShIfM a -> IO (Either (Messages TcRnMessage) a)
initRnIface hsc_env iface insts nsubst do_this = do
- errs_var <- newIORef emptyBag
+ errs_var <- newIORef emptyMessages
let hsubst = listToUFM insts
rn_mod = renameHoleModule (hsc_units hsc_env) hsubst
env = ShIfEnv {
@@ -201,9 +203,9 @@ initRnIface hsc_env iface insts nsubst do_this = do
res <- initTcRnIf 'c' hsc_env env () $ tryM do_this
msgs <- readIORef errs_var
case res of
- Left _ -> return (Left msgs)
- Right r | not (isEmptyBag msgs) -> return (Left msgs)
- | otherwise -> return (Right r)
+ Left _ -> return (Left msgs)
+ Right r | not (isEmptyMessages msgs) -> return (Left msgs)
+ | otherwise -> return (Right r)
-- | Environment for 'ShIfM' monads.
data ShIfEnv = ShIfEnv {
@@ -221,8 +223,8 @@ data ShIfEnv = ShIfEnv {
-- we just load the target interface and look at the export
-- list to determine the renaming.
sh_if_shape :: Maybe NameShape,
- -- Mutable reference to keep track of errors (similar to 'tcl_errs')
- sh_if_errs :: IORef ErrorMessages
+ -- Mutable reference to keep track of diagnostics (similar to 'tcl_errs')
+ sh_if_errs :: IORef (Messages TcRnMessage)
}
getHoleSubst :: ShIfM ShHoleSubst
diff --git a/compiler/GHC/Parser/Errors.hs b/compiler/GHC/Parser/Errors.hs
index e48f04aae5..570385c773 100644
--- a/compiler/GHC/Parser/Errors.hs
+++ b/compiler/GHC/Parser/Errors.hs
@@ -9,7 +9,7 @@ module GHC.Parser.Errors
, LexErr(..)
, CmmParserError(..)
, LexErrKind(..)
- , Hint(..)
+ , PsHint(..)
, StarIsType (..)
)
where
@@ -82,8 +82,8 @@ data TransLayoutReason
data PsError = PsError
{ errDesc :: !PsErrorDesc -- ^ Error description
- , errHints :: ![Hint] -- ^ Hints
- , errLoc :: !SrcSpan -- ^ Error position
+ , errHints :: ![PsHint] -- ^ Hints
+ , errLoc :: !SrcSpan -- ^ Error position
}
data PsErrorDesc
@@ -396,7 +396,7 @@ data NumUnderscoreReason
| NumUnderscore_Float
deriving (Show,Eq,Ord)
-data Hint
+data PsHint
= SuggestTH
| SuggestRecursiveDo
| SuggestDo
diff --git a/compiler/GHC/Parser/Errors/Ppr.hs b/compiler/GHC/Parser/Errors/Ppr.hs
index 0e83949a2e..3b73e068b4 100644
--- a/compiler/GHC/Parser/Errors/Ppr.hs
+++ b/compiler/GHC/Parser/Errors/Ppr.hs
@@ -2,15 +2,19 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleContexts #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-} -- instance Diagnostic PsMessage
+
module GHC.Parser.Errors.Ppr
( mkParserWarn
, mkParserErr
+ , pprPsError
)
where
import GHC.Prelude
import GHC.Driver.Flags
import GHC.Parser.Errors
+import GHC.Parser.Errors.Types
import GHC.Parser.Types
import GHC.Types.Basic
import GHC.Types.Error
@@ -27,26 +31,30 @@ import GHC.Builtin.Types (filterCTuple)
import GHC.Driver.Session (DynFlags)
import GHC.Utils.Error (diagReasonSeverity)
-mk_parser_err :: SrcSpan -> SDoc -> MsgEnvelope DiagnosticMessage
+instance Diagnostic PsMessage where
+ diagnosticMessage (PsUnknownMessage m) = diagnosticMessage m
+ diagnosticReason (PsUnknownMessage m) = diagnosticReason m
+
+mk_parser_err :: SrcSpan -> SDoc -> MsgEnvelope PsMessage
mk_parser_err span doc = MsgEnvelope
{ errMsgSpan = span
, errMsgContext = alwaysQualify
- , errMsgDiagnostic = DiagnosticMessage (mkDecorated [doc]) ErrorWithoutFlag
+ , errMsgDiagnostic = PsUnknownMessage $ DiagnosticMessage (mkDecorated [doc]) ErrorWithoutFlag
, errMsgSeverity = SevError
}
-mk_parser_warn :: DynFlags -> WarningFlag -> SrcSpan -> SDoc -> MsgEnvelope DiagnosticMessage
+mk_parser_warn :: DynFlags -> WarningFlag -> SrcSpan -> SDoc -> MsgEnvelope PsMessage
mk_parser_warn df flag span doc = MsgEnvelope
{ errMsgSpan = span
, errMsgContext = alwaysQualify
- , errMsgDiagnostic = DiagnosticMessage (mkDecorated [doc]) reason
+ , errMsgDiagnostic = PsUnknownMessage $ DiagnosticMessage (mkDecorated [doc]) reason
, errMsgSeverity = diagReasonSeverity df reason
}
where
reason :: DiagnosticReason
reason = WarningWithFlag flag
-mkParserWarn :: DynFlags -> PsWarning -> MsgEnvelope DiagnosticMessage
+mkParserWarn :: DynFlags -> PsWarning -> MsgEnvelope PsMessage
mkParserWarn df = \case
PsWarnTab loc tc
-> mk_parser_warn df Opt_WarnTabs loc $
@@ -132,9 +140,13 @@ mkParserWarn df = \case
OperatorWhitespaceOccurrence_Suffix -> mk_msg "suffix"
OperatorWhitespaceOccurrence_TightInfix -> mk_msg "tight infix"
-mkParserErr :: PsError -> MsgEnvelope DiagnosticMessage
-mkParserErr err = mk_parser_err (errLoc err) $ vcat
- (pp_err (errDesc err) : map pp_hint (errHints err))
+mkParserErr :: PsError -> MsgEnvelope PsMessage
+mkParserErr err = mk_parser_err (errLoc err) $
+ pprPsError (errDesc err) (errHints err)
+
+-- | Render a 'PsErrorDesc' into an 'SDoc', with its 'PsHint's.
+pprPsError :: PsErrorDesc -> [PsHint] -> SDoc
+pprPsError desc hints = vcat (pp_err desc : map pp_hint hints)
pp_err :: PsErrorDesc -> SDoc
pp_err = \case
@@ -602,7 +614,7 @@ pp_unexpected_fun_app e a =
$$ text "You could write it with parentheses"
$$ text "Or perhaps you meant to enable BlockArguments?"
-pp_hint :: Hint -> SDoc
+pp_hint :: PsHint -> SDoc
pp_hint = \case
SuggestTH -> text "Perhaps you intended to use TemplateHaskell"
SuggestDo -> text "Perhaps this statement should be within a 'do' block?"
diff --git a/compiler/GHC/Parser/Errors/Types.hs b/compiler/GHC/Parser/Errors/Types.hs
new file mode 100644
index 0000000000..293dcc3ee0
--- /dev/null
+++ b/compiler/GHC/Parser/Errors/Types.hs
@@ -0,0 +1,9 @@
+
+module GHC.Parser.Errors.Types where
+
+import GHC.Types.Error
+
+data PsMessage
+ = PsUnknownMessage !DiagnosticMessage
+ -- ^ Simply rewraps a generic 'DiagnosticMessage'. More
+ -- constructors will be added in the future (#18516).
diff --git a/compiler/GHC/Parser/Header.hs b/compiler/GHC/Parser/Header.hs
index 84cbb5e0d4..02503924ee 100644
--- a/compiler/GHC/Parser/Header.hs
+++ b/compiler/GHC/Parser/Header.hs
@@ -16,7 +16,7 @@ module GHC.Parser.Header
, mkPrelImports -- used by the renamer too
, getOptionsFromFile
, getOptions
- , optionsErrorMsgs
+ , toArgs
, checkProcessArgsResult
)
where
@@ -29,7 +29,9 @@ import GHC.Platform
import GHC.Driver.Session
import GHC.Driver.Config
+import GHC.Driver.Errors.Types -- Unfortunate, needed due to the fact we throw exceptions!
+import GHC.Parser.Errors.Types
import GHC.Parser.Errors.Ppr
import GHC.Parser.Errors
import GHC.Parser ( parseHeader )
@@ -39,7 +41,7 @@ import GHC.Hs
import GHC.Unit.Module
import GHC.Builtin.Names
-import GHC.Types.Error hiding ( getMessages, getErrorMessages, getWarningMessages )
+import GHC.Types.Error hiding ( getErrorMessages, getWarningMessages, getMessages )
import GHC.Types.SrcLoc
import GHC.Types.SourceError
import GHC.Types.SourceText
@@ -53,13 +55,17 @@ import GHC.Utils.Exception as Exception
import GHC.Data.StringBuffer
import GHC.Data.Maybe
-import GHC.Data.Bag ( Bag, listToBag, unitBag, isEmptyBag )
+import GHC.Data.Bag (Bag, isEmptyBag )
import GHC.Data.FastString
import Control.Monad
import System.IO
import System.IO.Unsafe
import Data.List (partition)
+import Data.Char (isSpace)
+import Text.ParserCombinators.ReadP (readP_to_S, gather)
+import Text.ParserCombinators.ReadPrec (readPrec_to_P)
+import Text.Read (readPrec)
------------------------------------------------------------------------------
@@ -91,7 +97,7 @@ getImports popts implicit_prelude buf filename source_filename = do
-- don't log warnings: they'll be reported when we parse the file
-- for real. See #2500.
if not (isEmptyBag errs)
- then throwIO $ mkSrcErr (fmap mkParserErr errs)
+ then throwErrors $ foldPsMessages mkParserErr errs
else
let hsmod = unLoc rdr_module
mb_mod = hsmodName hsmod
@@ -260,10 +266,14 @@ getOptions' dflags toks
parseToks (open:close:xs)
| IToptions_prag str <- unLoc open
, ITclose_prag <- unLoc close
- = case toArgs str of
+ = case toArgs starting_loc str of
Left _err -> optionsParseError str $ -- #15053
combineSrcSpans (getLoc open) (getLoc close)
- Right args -> map (L (getLoc open)) args ++ parseToks xs
+ Right args -> args ++ parseToks xs
+ where
+ src_span = getLoc open
+ real_src_span = expectJust "getOptions'" (srcSpanToRealSrcSpan src_span)
+ starting_loc = realSrcSpanStart real_src_span
parseToks (open:close:xs)
| ITinclude_prag str <- unLoc open
, ITclose_prag <- unLoc close
@@ -304,6 +314,107 @@ getOptions' dflags toks
(ITdocSection {}) -> True
_ -> False
+toArgs :: RealSrcLoc
+ -> String -> Either String -- Error
+ [Located String] -- Args
+toArgs starting_loc orig_str
+ = let (after_spaces_loc, after_spaces_str) = consume_spaces starting_loc orig_str in
+ case after_spaces_str of
+ '[':after_bracket ->
+ let after_bracket_loc = advanceSrcLoc after_spaces_loc '['
+ (after_bracket_spaces_loc, after_bracket_spaces_str)
+ = consume_spaces after_bracket_loc after_bracket in
+ case after_bracket_spaces_str of
+ ']':rest | all isSpace rest -> Right []
+ _ -> readAsList after_bracket_spaces_loc after_bracket_spaces_str
+
+ _ -> toArgs' after_spaces_loc after_spaces_str
+ where
+ consume_spaces :: RealSrcLoc -> String -> (RealSrcLoc, String)
+ consume_spaces loc [] = (loc, [])
+ consume_spaces loc (c:cs)
+ | isSpace c = consume_spaces (advanceSrcLoc loc c) cs
+ | otherwise = (loc, c:cs)
+
+ break_with_loc :: (Char -> Bool) -> RealSrcLoc -> String
+ -> (String, RealSrcLoc, String) -- location is start of second string
+ break_with_loc p = go []
+ where
+ go reversed_acc loc [] = (reverse reversed_acc, loc, [])
+ go reversed_acc loc (c:cs)
+ | p c = (reverse reversed_acc, loc, c:cs)
+ | otherwise = go (c:reversed_acc) (advanceSrcLoc loc c) cs
+
+ advance_src_loc_many :: RealSrcLoc -> String -> RealSrcLoc
+ advance_src_loc_many = foldl' advanceSrcLoc
+
+ locate :: RealSrcLoc -> RealSrcLoc -> a -> Located a
+ locate begin end x = L (RealSrcSpan (mkRealSrcSpan begin end) Nothing) x
+
+ toArgs' :: RealSrcLoc -> String -> Either String [Located String]
+ -- Remove outer quotes:
+ -- > toArgs' "\"foo\" \"bar baz\""
+ -- Right ["foo", "bar baz"]
+ --
+ -- Keep inner quotes:
+ -- > toArgs' "-DFOO=\"bar baz\""
+ -- Right ["-DFOO=\"bar baz\""]
+ toArgs' loc s =
+ let (after_spaces_loc, after_spaces_str) = consume_spaces loc s in
+ case after_spaces_str of
+ [] -> Right []
+ '"' : _ -> do
+ -- readAsString removes outer quotes
+ (arg, new_loc, rest) <- readAsString after_spaces_loc after_spaces_str
+ check_for_space rest
+ (locate after_spaces_loc new_loc arg:)
+ `fmap` toArgs' new_loc rest
+ _ -> case break_with_loc (isSpace <||> (== '"')) after_spaces_loc after_spaces_str of
+ (argPart1, loc2, s''@('"':_)) -> do
+ (argPart2, loc3, rest) <- readAsString loc2 s''
+ check_for_space rest
+ -- show argPart2 to keep inner quotes
+ (locate after_spaces_loc loc3 (argPart1 ++ show argPart2):)
+ `fmap` toArgs' loc3 rest
+ (arg, loc2, s'') -> (locate after_spaces_loc loc2 arg:)
+ `fmap` toArgs' loc2 s''
+
+ check_for_space :: String -> Either String ()
+ check_for_space [] = Right ()
+ check_for_space (c:_)
+ | isSpace c = Right ()
+ | otherwise = Left ("Whitespace expected after string in " ++ show orig_str)
+
+ reads_with_consumed :: Read a => String
+ -> [((String, a), String)]
+ -- ((consumed string, parsed result), remainder of input)
+ reads_with_consumed = readP_to_S (gather (readPrec_to_P readPrec 0))
+
+ readAsString :: RealSrcLoc
+ -> String
+ -> Either String (String, RealSrcLoc, String)
+ readAsString loc s = case reads_with_consumed s of
+ [((consumed, arg), rest)] ->
+ Right (arg, advance_src_loc_many loc consumed, rest)
+ _ ->
+ Left ("Couldn't read " ++ show s ++ " as String")
+
+ -- input has had the '[' stripped off
+ readAsList :: RealSrcLoc -> String -> Either String [Located String]
+ readAsList loc s = do
+ let (after_spaces_loc, after_spaces_str) = consume_spaces loc s
+ (arg, after_arg_loc, after_arg_str) <- readAsString after_spaces_loc after_spaces_str
+ let (after_arg_spaces_loc, after_arg_spaces_str)
+ = consume_spaces after_arg_loc after_arg_str
+ (locate after_spaces_loc after_arg_loc arg :) <$>
+ case after_arg_spaces_str of
+ ',':after_comma -> readAsList (advanceSrcLoc after_arg_spaces_loc ',') after_comma
+ ']':after_bracket
+ | all isSpace after_bracket
+ -> Right []
+ _ -> Left ("Couldn't read " ++ show ('[' : s) ++ " as [String]")
+ -- reinsert missing '[' for clarity.
+
-----------------------------------------------------------------------------
-- | Complain about non-dynamic flags in OPTIONS pragmas.
@@ -313,11 +424,12 @@ getOptions' dflags toks
checkProcessArgsResult :: MonadIO m => [Located String] -> m ()
checkProcessArgsResult flags
= when (notNull flags) $
- liftIO $ throwIO $ mkSrcErr $ listToBag $ map mkMsg flags
+ liftIO $ throwErrors $ foldMap (singleMessage . mkMsg) flags
where mkMsg (L loc flag)
= mkPlainErrorMsgEnvelope loc $
- (text "unknown flag in {-# OPTIONS_GHC #-} pragma:" <+>
- text flag)
+ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError $
+ text "unknown flag in {-# OPTIONS_GHC #-} pragma:" <+>
+ text flag
-----------------------------------------------------------------------------
@@ -349,19 +461,6 @@ unsupportedExtnError dflags loc unsup =
supported = supportedLanguagesAndExtensions $ platformArchOS $ targetPlatform dflags
suggestions = fuzzyMatch unsup supported
-
-optionsErrorMsgs :: [String] -> [Located String] -> FilePath -> Messages DiagnosticMessage
-optionsErrorMsgs unhandled_flags flags_lines _filename
- = mkMessages $ listToBag (map mkMsg unhandled_flags_lines)
- where unhandled_flags_lines :: [Located String]
- unhandled_flags_lines = [ L l f
- | f <- unhandled_flags
- , L l f' <- flags_lines
- , f == f' ]
- mkMsg (L flagSpan flag) =
- mkPlainErrorMsgEnvelope flagSpan $
- text "unknown flag in {-# OPTIONS_GHC #-} pragma:" <+> text flag
-
optionsParseError :: String -> SrcSpan -> a -- #15053
optionsParseError str loc =
throwErr loc $
@@ -372,4 +471,5 @@ optionsParseError str loc =
throwErr :: SrcSpan -> SDoc -> a -- #15053
throwErr loc doc =
- throw $ mkSrcErr $ unitBag $ mkPlainErrorMsgEnvelope loc doc
+ let msg = mkPlainErrorMsgEnvelope loc $ GhcPsMessage $ PsUnknownMessage $ mkPlainError doc
+ in throw $ mkSrcErr $ singleMessage msg
diff --git a/compiler/GHC/Parser/PostProcess.hs b/compiler/GHC/Parser/PostProcess.hs
index 6411df34d9..0ffc3125e6 100644
--- a/compiler/GHC/Parser/PostProcess.hs
+++ b/compiler/GHC/Parser/PostProcess.hs
@@ -1078,7 +1078,7 @@ checkImportDecl mPre mPost = do
checkPattern :: LocatedA (PatBuilder GhcPs) -> P (LPat GhcPs)
checkPattern = runPV . checkLPat
-checkPattern_hints :: [Hint] -> PV (LocatedA (PatBuilder GhcPs)) -> P (LPat GhcPs)
+checkPattern_hints :: [PsHint] -> PV (LocatedA (PatBuilder GhcPs)) -> P (LPat GhcPs)
checkPattern_hints hints pp = runPV_hints hints (pp >>= checkLPat)
checkLPat :: LocatedA (PatBuilder GhcPs) -> PV (LPat GhcPs)
@@ -2721,7 +2721,7 @@ failOpFewArgs (L loc op) =
data PV_Context =
PV_Context
{ pv_options :: ParserOpts
- , pv_hints :: [Hint] -- See Note [Parser-Validator Hint]
+ , pv_hints :: [PsHint] -- See Note [Parser-Validator Hint]
}
data PV_Accum =
@@ -2771,7 +2771,7 @@ instance Monad PV where
runPV :: PV a -> P a
runPV = runPV_hints []
-runPV_hints :: [Hint] -> PV a -> P a
+runPV_hints :: [PsHint] -> PV a -> P a
runPV_hints hints m =
P $ \s ->
let
@@ -2792,7 +2792,7 @@ runPV_hints hints m =
PV_Ok acc' a -> POk (mkPState acc') a
PV_Failed acc' -> PFailed (mkPState acc')
-add_hint :: Hint -> PV a -> PV a
+add_hint :: PsHint -> PV a -> PV a
add_hint hint m =
let modifyHint ctx = ctx{pv_hints = pv_hints ctx ++ [hint]} in
PV (\ctx acc -> unPV m (modifyHint ctx) acc)
diff --git a/compiler/GHC/Runtime/Eval.hs b/compiler/GHC/Runtime/Eval.hs
index d8c8c781da..80868c1eea 100644
--- a/compiler/GHC/Runtime/Eval.hs
+++ b/compiler/GHC/Runtime/Eval.hs
@@ -50,6 +50,7 @@ import GHC.Prelude
import GHC.Driver.Monad
import GHC.Driver.Main
+import GHC.Driver.Errors.Types ( hoistTcRnMessage )
import GHC.Driver.Env
import GHC.Driver.Session
import GHC.Driver.Ppr
@@ -1032,7 +1033,7 @@ typeKind normalise str = withSession $ \hsc_env ->
getInstancesForType :: GhcMonad m => Type -> m [ClsInst]
getInstancesForType ty = withSession $ \hsc_env ->
liftIO $ runInteractiveHsc hsc_env $
- ioMsgMaybe $ runTcInteractive hsc_env $ do
+ ioMsgMaybe $ hoistTcRnMessage $ runTcInteractive hsc_env $ do
-- Bring class and instances from unqualified modules into scope, this fixes #16793.
loadUnqualIfaces hsc_env (hsc_IC hsc_env)
matches <- findMatchingInstances ty
@@ -1045,7 +1046,7 @@ parseInstanceHead str = withSession $ \hsc_env0 -> do
(ty, _) <- liftIO $ runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
ty <- hscParseType str
- ioMsgMaybe $ tcRnType hsc_env SkolemiseFlexi True ty
+ ioMsgMaybe $ hoistTcRnMessage $ tcRnType hsc_env SkolemiseFlexi True ty
return ty
diff --git a/compiler/GHC/Tc/Deriv.hs b/compiler/GHC/Tc/Deriv.hs
index 40761ed38c..c71ad4b7b8 100644
--- a/compiler/GHC/Tc/Deriv.hs
+++ b/compiler/GHC/Tc/Deriv.hs
@@ -2007,7 +2007,7 @@ doDerivInstErrorChecks2 clas clas_inst theta wildcard mechanism
-- See Note [Deriving strategies]
; when (exotic_mechanism && className clas `elem` genericClassNames) $
do { failIfTc (safeLanguageOn dflags) gen_inst_err
- ; when (safeInferOn dflags) (recordUnsafeInfer emptyBag) } }
+ ; when (safeInferOn dflags) (recordUnsafeInfer emptyMessages) } }
where
exotic_mechanism = not $ isDerivSpecStock mechanism
diff --git a/compiler/GHC/Tc/Errors.hs b/compiler/GHC/Tc/Errors.hs
index 1f972c6425..9de37b0313 100644
--- a/compiler/GHC/Tc/Errors.hs
+++ b/compiler/GHC/Tc/Errors.hs
@@ -1029,26 +1029,26 @@ mkErrorReport :: DiagnosticReason
-> ReportErrCtxt
-> TcLclEnv
-> Report
- -> TcM (MsgEnvelope DiagnosticMessage)
+ -> TcM (MsgEnvelope TcRnMessage)
mkErrorReport rea ctxt tcl_env (Report important relevant_bindings valid_subs)
= do { context <- mkErrInfo (cec_tidy ctxt) (tcl_ctxt tcl_env)
- ; mkDecoratedSDocAt rea
- (RealSrcSpan (tcl_loc tcl_env) Nothing)
- (vcat important)
- context
- (vcat $ relevant_bindings ++ valid_subs)
+ ; mkTcRnMessage rea
+ (RealSrcSpan (tcl_loc tcl_env) Nothing)
+ (vcat important)
+ context
+ (vcat $ relevant_bindings ++ valid_subs)
}
-- This version does not include the context
mkErrorReportNC :: DiagnosticReason
-> TcLclEnv
-> Report
- -> TcM (MsgEnvelope DiagnosticMessage)
+ -> TcM (MsgEnvelope TcRnMessage)
mkErrorReportNC rea tcl_env (Report important relevant_bindings valid_subs)
- = mkDecoratedSDocAt rea (RealSrcSpan (tcl_loc tcl_env) Nothing)
- (vcat important)
- O.empty
- (vcat $ relevant_bindings ++ valid_subs)
+ = mkTcRnMessage rea (RealSrcSpan (tcl_loc tcl_env) Nothing)
+ (vcat important)
+ O.empty
+ (vcat $ relevant_bindings ++ valid_subs)
type UserGiven = Implication
@@ -1186,7 +1186,7 @@ See also 'reportUnsolved'.
----------------
-- | Constructs a new hole error, unless this is deferred. See Note [Constructing Hole Errors].
-mkHoleError :: NameEnv Type -> [Ct] -> ReportErrCtxt -> Hole -> TcM (MsgEnvelope DiagnosticMessage)
+mkHoleError :: NameEnv Type -> [Ct] -> ReportErrCtxt -> Hole -> TcM (MsgEnvelope TcRnMessage)
mkHoleError _ _tidy_simples ctxt hole@(Hole { hole_occ = occ
, hole_ty = hole_ty
, hole_loc = ct_loc })
@@ -1310,7 +1310,6 @@ mkHoleError lcl_name_cache tidy_simples ctxt hole@(Hole { hole_occ = occ
When working with typed holes we have to deal with the case where
we want holes to be reported as warnings to users during compile time but
as errors during runtime. Therefore, we have to call 'maybeAddDeferredBindings'
-with a function which is able to override the 'DiagnosticReason' of a 'DiagnosticMessage',
so that the correct 'Severity' can be computed out of that later on.
-}
diff --git a/compiler/GHC/Tc/Errors/Ppr.hs b/compiler/GHC/Tc/Errors/Ppr.hs
new file mode 100644
index 0000000000..c6da9f1b9b
--- /dev/null
+++ b/compiler/GHC/Tc/Errors/Ppr.hs
@@ -0,0 +1,10 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-} -- instance Diagnostic TcRnMessage
+
+module GHC.Tc.Errors.Ppr where
+
+import GHC.Tc.Errors.Types
+import GHC.Types.Error
+
+instance Diagnostic TcRnMessage where
+ diagnosticMessage (TcRnUnknownMessage m) = diagnosticMessage m
+ diagnosticReason (TcRnUnknownMessage m) = diagnosticReason m
diff --git a/compiler/GHC/Tc/Errors/Types.hs b/compiler/GHC/Tc/Errors/Types.hs
new file mode 100644
index 0000000000..1241735191
--- /dev/null
+++ b/compiler/GHC/Tc/Errors/Types.hs
@@ -0,0 +1,12 @@
+module GHC.Tc.Errors.Types (
+ -- * Main types
+ TcRnMessage(..)
+ ) where
+
+import GHC.Types.Error
+
+-- | An error which might arise during typechecking/renaming.
+data TcRnMessage
+ = TcRnUnknownMessage !DiagnosticMessage
+ -- ^ Simply rewraps a generic 'DiagnosticMessage'. More
+ -- constructors will be added in the future (#18516).
diff --git a/compiler/GHC/Tc/Gen/Foreign.hs b/compiler/GHC/Tc/Gen/Foreign.hs
index 57b99e703a..166f366038 100644
--- a/compiler/GHC/Tc/Gen/Foreign.hs
+++ b/compiler/GHC/Tc/Gen/Foreign.hs
@@ -475,7 +475,7 @@ checkForeignRes non_io_result_ok check_safe pred_res_ty ty
-- handle safe infer fail
_ | check_safe && safeInferOn dflags
- -> recordUnsafeInfer emptyBag
+ -> recordUnsafeInfer emptyMessages
-- handle safe language typecheck fail
_ | check_safe && safeLanguageOn dflags
diff --git a/compiler/GHC/Tc/Gen/Splice.hs b/compiler/GHC/Tc/Gen/Splice.hs
index 589513af97..7124dcd52e 100644
--- a/compiler/GHC/Tc/Gen/Splice.hs
+++ b/compiler/GHC/Tc/Gen/Splice.hs
@@ -34,6 +34,7 @@ module GHC.Tc.Gen.Splice(
import GHC.Prelude
+import GHC.Driver.Errors
import GHC.Driver.Plugins
import GHC.Driver.Main
import GHC.Driver.Session
@@ -42,6 +43,7 @@ import GHC.Driver.Hooks
import GHC.Hs
+import GHC.Tc.Errors.Types
import GHC.Tc.Utils.Monad
import GHC.Tc.Utils.TcType
import GHC.Tc.Gen.Expr
@@ -917,6 +919,48 @@ runMetaD :: LHsExpr GhcTc -- Of type Q [Dec]
-> TcM [LHsDecl GhcPs]
runMetaD = runMeta metaRequestD
+{- Note [Errors in desugaring a splice]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+What should we do if there are errors when desugaring a splice? We should
+abort. There are several cases to consider:
+
+(a) The desugarer hits an unrecoverable error and fails in the monad.
+(b) The desugarer hits a recoverable error, reports it, and continues.
+(c) The desugarer reports a fatal warning (with -Werror), reports it, and continues.
+(d) The desugarer reports a non-fatal warning, and continues.
+
+Each case is tested in th/T19709[abcd].
+
+General principle: we wish to report all messages from dealing with a splice
+eagerly, as these messages arise during an earlier stage than type-checking
+generally. It's also likely that a compile-time warning from spliced code
+will be easier to understand then an error that arises from processing the
+code the splice produces. (Rationale: the warning will be about the code the
+user actually wrote, not what is generated.)
+
+Case (a): We have no choice but to abort here, but we must make sure that
+the messages are printed or logged before aborting. Logging them is annoying,
+because we're in the type-checker, and the messages are DsMessages, from the
+desugarer. So we report and then fail in the monad. This case is detected
+by the fact that initDsTc returns Nothing.
+
+Case (b): We detect this case by looking for errors in the messages returned
+from initDsTc and aborting if we spot any (after printing, of course). Note
+that initDsTc will return a Just ds_expr in this case, but we don't wish to
+use the (likely very bogus) expression.
+
+Case (c): This is functionally the same as (b), except that the expression
+isn't bogus. We still don't wish to use it, as the user's request for -Werror
+tells us not to.
+
+Case (d): We report the warnings and then carry on with the expression.
+This might result in warnings printed out of source order, but this is
+appropriate, as the warnings from the splice arise from an earlier stage
+of compilation.
+
+Previously, we failed to abort in cases (b) and (c), leading to #19709.
+-}
+
---------------
runMeta' :: Bool -- Whether code should be printed in the exception message
-> (hs_syn -> SDoc) -- how to print the code
@@ -932,11 +976,11 @@ runMeta' show_code ppr_hs run_and_convert expr
-- Check that we've had no errors of any sort so far.
-- For example, if we found an error in an earlier defn f, but
-- recovered giving it type f :: forall a.a, it'd be very dodgy
- -- to carry ont. Mind you, the staging restrictions mean we won't
+ -- to carry on. Mind you, the staging restrictions mean we won't
-- actually run f, but it still seems wrong. And, more concretely,
-- see #5358 for an example that fell over when trying to
-- reify a function with a "?" kind in it. (These don't occur
- -- in type-correct programs.
+ -- in type-correct programs.)
; failIfErrsM
-- run plugins
@@ -944,7 +988,23 @@ runMeta' show_code ppr_hs run_and_convert expr
; expr' <- withPlugins hsc_env spliceRunAction expr
-- Desugar
- ; ds_expr <- initDsTc (dsLExpr expr')
+ ; (ds_msgs, mb_ds_expr) <- initDsTc (dsLExpr expr')
+
+ -- Print any messages (even warnings) eagerly: they might be helpful if anything
+ -- goes wrong. See Note [Errors in desugaring a splice]. This happens in all
+ -- cases.
+ ; logger <- getLogger
+ ; dflags <- getDynFlags
+ ; liftIO $ printMessages logger dflags ds_msgs
+
+ ; ds_expr <- case mb_ds_expr of
+ Nothing -> failM -- Case (a) from Note [Errors in desugaring a splice]
+ Just ds_expr -> -- There still might be a fatal warning or recoverable
+ -- Cases (b) and (c) from Note [Errors in desugaring a splice]
+ do { when (errorsOrFatalWarningsFound ds_msgs)
+ failM
+ ; return ds_expr }
+
-- Compile and link it; might fail if linking fails
; src_span <- getSrcSpanM
; traceTc "About to run (desugared)" (ppr ds_expr)
@@ -1442,7 +1502,7 @@ runTH ty fhv = do
-- See Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.
runRemoteTH
:: IServInstance
- -> [Messages DiagnosticMessage] -- saved from nested calls to qRecover
+ -> [Messages TcRnMessage] -- saved from nested calls to qRecover
-> TcM ()
runRemoteTH iserv recovers = do
THMsg msg <- liftIO $ readIServ iserv getTHMessage
diff --git a/compiler/GHC/Tc/Module.hs b/compiler/GHC/Tc/Module.hs
index 777086343b..0511e1e268 100644
--- a/compiler/GHC/Tc/Module.hs
+++ b/compiler/GHC/Tc/Module.hs
@@ -54,6 +54,7 @@ import GHC.Driver.Plugins
import GHC.Driver.Session
import GHC.Tc.Errors.Hole.FitTypes ( HoleFitPluginR (..) )
+import GHC.Tc.Errors.Types
import {-# SOURCE #-} GHC.Tc.Gen.Splice ( finishTH, runRemoteModFinalizers )
import GHC.Tc.Gen.HsType
import GHC.Tc.Validity( checkValidType )
@@ -191,7 +192,7 @@ tcRnModule :: HscEnv
-> ModSummary
-> Bool -- True <=> save renamed syntax
-> HsParsedModule
- -> IO (Messages DiagnosticMessage, Maybe TcGblEnv)
+ -> IO (Messages TcRnMessage, Maybe TcGblEnv)
tcRnModule hsc_env mod_sum save_rn_syntax
parsedModule@HsParsedModule {hpm_module= L loc this_module}
@@ -213,7 +214,8 @@ tcRnModule hsc_env mod_sum save_rn_syntax
logger = hsc_logger hsc_env
home_unit = hsc_home_unit hsc_env
err_msg = mkPlainErrorMsgEnvelope loc $
- text "Module does not have a RealSrcSpan:" <+> ppr this_mod
+ TcRnUnknownMessage $ mkPlainError $
+ text "Module does not have a RealSrcSpan:" <+> ppr this_mod
pair :: (Module, SrcSpan)
pair@(this_mod,_)
@@ -2010,7 +2012,7 @@ get two defns for 'main' in the interface file!
*********************************************************
-}
-runTcInteractive :: HscEnv -> TcRn a -> IO (Messages DiagnosticMessage, Maybe a)
+runTcInteractive :: HscEnv -> TcRn a -> IO (Messages TcRnMessage, Maybe a)
-- Initialise the tcg_inst_env with instances from all home modules.
-- This mimics the more selective call to hptInstances in tcRnImports
runTcInteractive hsc_env thing_inside
@@ -2126,7 +2128,7 @@ We don't bother with the tcl_th_bndrs environment either.
-- The returned TypecheckedHsExpr is of type IO [ () ], a list of the bound
-- values, coerced to ().
tcRnStmt :: HscEnv -> GhciLStmt GhcPs
- -> IO (Messages DiagnosticMessage, Maybe ([Id], LHsExpr GhcTc, FixityEnv))
+ -> IO (Messages TcRnMessage, Maybe ([Id], LHsExpr GhcTc, FixityEnv))
tcRnStmt hsc_env rdr_stmt
= runTcInteractive hsc_env $ do {
@@ -2507,7 +2509,7 @@ getGhciStepIO = do
return (noLocA $ ExprWithTySig noExtField (nlHsVar ghciStepIoMName) stepTy)
-isGHCiMonad :: HscEnv -> String -> IO (Messages DiagnosticMessage, Maybe Name)
+isGHCiMonad :: HscEnv -> String -> IO (Messages TcRnMessage, Maybe Name)
isGHCiMonad hsc_env ty
= runTcInteractive hsc_env $ do
rdrEnv <- getGlobalRdrEnv
@@ -2534,7 +2536,7 @@ data TcRnExprMode = TM_Inst -- ^ Instantiate inferred quantifiers only (:typ
tcRnExpr :: HscEnv
-> TcRnExprMode
-> LHsExpr GhcPs
- -> IO (Messages DiagnosticMessage, Maybe Type)
+ -> IO (Messages TcRnMessage, Maybe Type)
tcRnExpr hsc_env mode rdr_expr
= runTcInteractive hsc_env $
do {
@@ -2603,7 +2605,7 @@ has a special case for application chains.
--------------------------
tcRnImportDecls :: HscEnv
-> [LImportDecl GhcPs]
- -> IO (Messages DiagnosticMessage, Maybe GlobalRdrEnv)
+ -> IO (Messages TcRnMessage, Maybe GlobalRdrEnv)
-- Find the new chunk of GlobalRdrEnv created by this list of import
-- decls. In contract tcRnImports *extends* the TcGblEnv.
tcRnImportDecls hsc_env import_decls
@@ -2619,7 +2621,7 @@ tcRnType :: HscEnv
-> ZonkFlexi
-> Bool -- Normalise the returned type
-> LHsType GhcPs
- -> IO (Messages DiagnosticMessage, Maybe (Type, Kind))
+ -> IO (Messages TcRnMessage, Maybe (Type, Kind))
tcRnType hsc_env flexi normalise rdr_type
= runTcInteractive hsc_env $
setXOptM LangExt.PolyKinds $ -- See Note [Kind-generalise in tcRnType]
@@ -2753,7 +2755,7 @@ tcRnDeclsi exists to allow class, data, and other declarations in GHCi.
tcRnDeclsi :: HscEnv
-> [LHsDecl GhcPs]
- -> IO (Messages DiagnosticMessage, Maybe TcGblEnv)
+ -> IO (Messages TcRnMessage, Maybe TcGblEnv)
tcRnDeclsi hsc_env local_decls
= runTcInteractive hsc_env $
tcRnSrcDecls False Nothing local_decls
@@ -2778,13 +2780,13 @@ externaliseAndTidyId this_mod id
-- a package module with an interface on disk. If neither of these is
-- true, then the result will be an error indicating the interface
-- could not be found.
-getModuleInterface :: HscEnv -> Module -> IO (Messages DiagnosticMessage, Maybe ModIface)
+getModuleInterface :: HscEnv -> Module -> IO (Messages TcRnMessage, Maybe ModIface)
getModuleInterface hsc_env mod
= runTcInteractive hsc_env $
loadModuleInterface (text "getModuleInterface") mod
tcRnLookupRdrName :: HscEnv -> LocatedN RdrName
- -> IO (Messages DiagnosticMessage, Maybe [Name])
+ -> IO (Messages TcRnMessage, Maybe [Name])
-- ^ Find all the Names that this RdrName could mean, in GHCi
tcRnLookupRdrName hsc_env (L loc rdr_name)
= runTcInteractive hsc_env $
@@ -2798,7 +2800,7 @@ tcRnLookupRdrName hsc_env (L loc rdr_name)
; when (null names) (addErrTc (text "Not in scope:" <+> quotes (ppr rdr_name)))
; return names }
-tcRnLookupName :: HscEnv -> Name -> IO (Messages DiagnosticMessage, Maybe TyThing)
+tcRnLookupName :: HscEnv -> Name -> IO (Messages TcRnMessage, Maybe TyThing)
tcRnLookupName hsc_env name
= runTcInteractive hsc_env $
tcRnLookupName' name
@@ -2817,7 +2819,7 @@ tcRnLookupName' name = do
tcRnGetInfo :: HscEnv
-> Name
- -> IO ( Messages DiagnosticMessage
+ -> IO ( Messages TcRnMessage
, Maybe (TyThing, Fixity, [ClsInst], [FamInst], SDoc))
-- Used to implement :info in GHCi
@@ -3147,5 +3149,9 @@ mark_plugin_unsafe dflags = unless (gopt Opt_PluginTrustworthy dflags) $
recordUnsafeInfer pluginUnsafe
where
unsafeText = "Use of plugins makes the module unsafe"
- pluginUnsafe = unitBag ( mkPlainMsgEnvelope dflags WarningWithoutFlag noSrcSpan
- (Outputable.text unsafeText) )
+ pluginUnsafe =
+ singleMessage $
+ mkPlainMsgEnvelope dflags noSrcSpan $
+ TcRnUnknownMessage $
+ mkPlainDiagnostic WarningWithoutFlag $
+ Outputable.text unsafeText
diff --git a/compiler/GHC/Tc/Solver.hs b/compiler/GHC/Tc/Solver.hs
index 5e79a75472..76ce179b9d 100644
--- a/compiler/GHC/Tc/Solver.hs
+++ b/compiler/GHC/Tc/Solver.hs
@@ -152,7 +152,7 @@ simplifyTop wanteds
; whyUnsafe <- getWarningMessages <$> TcM.readTcRef errs_var
; TcM.writeTcRef errs_var saved_msg
- ; recordUnsafeInfer whyUnsafe
+ ; recordUnsafeInfer (mkMessages whyUnsafe)
}
; traceTc "reportUnsolved (unsafe overlapping) }" empty
@@ -708,10 +708,10 @@ How is this implemented? It's complicated! So we'll step through it all:
available and how they overlap. So we once again call `lookupInstEnv` to
figure that out so we can generate a helpful error message.
- 6) `GHC.Tc.Utils.Monad.recordUnsafeInfer` -- Save the unsafe result and reason in an
- IORef called `tcg_safeInfer`.
+ 6) `GHC.Tc.Utils.Monad.recordUnsafeInfer` -- Save the unsafe result and reason in
+ IORefs called `tcg_safe_infer` and `tcg_safe_infer_reason`.
- 7) `GHC.Driver.Main.tcRnModule'` -- Reads `tcg_safeInfer` after type-checking, calling
+ 7) `GHC.Driver.Main.tcRnModule'` -- Reads `tcg_safe_infer` after type-checking, calling
`GHC.Driver.Main.markUnsafeInfer` (passing the reason along) when safe-inferrence
failed.
diff --git a/compiler/GHC/Tc/Types.hs b/compiler/GHC/Tc/Types.hs
index 40cdf54d12..8e9e1db1b7 100644
--- a/compiler/GHC/Tc/Types.hs
+++ b/compiler/GHC/Tc/Types.hs
@@ -81,7 +81,10 @@ module GHC.Tc.Types(
lookupRoleAnnot, getRoleAnnots,
-- Linting
- lintGblEnv
+ lintGblEnv,
+
+ -- Diagnostics
+ TcRnMessage
) where
#include "HsVersions.h"
@@ -100,6 +103,7 @@ import GHC.Tc.Types.Constraint
import GHC.Tc.Types.Origin
import GHC.Tc.Types.Evidence
import {-# SOURCE #-} GHC.Tc.Errors.Hole.FitTypes ( HoleFitPlugin )
+import GHC.Tc.Errors.Types
import GHC.Core.Type
import GHC.Core.TyCon ( TyCon, tyConKind )
@@ -130,7 +134,6 @@ import GHC.Types.Unique.FM
import GHC.Types.Basic
import GHC.Types.CostCentre.State
import GHC.Types.HpcInfo
-import GHC.Types.Error ( DiagnosticMessage )
import GHC.Data.IOEnv
import GHC.Data.Bag
@@ -560,11 +563,18 @@ data TcGblEnv
-- function, if this module is
-- the main module.
- tcg_safeInfer :: TcRef (Bool, WarningMessages),
- -- ^ Has the typechecker inferred this module as -XSafe (Safe Haskell)
+ tcg_safe_infer :: TcRef Bool,
+ -- ^ Has the typechecker inferred this module as -XSafe (Safe Haskell)?
-- See Note [Safe Haskell Overlapping Instances Implementation],
-- although this is used for more than just that failure case.
+ tcg_safe_infer_reasons :: TcRef (Messages TcRnMessage),
+ -- ^ Unreported reasons why tcg_safe_infer is False.
+ -- INVARIANT: If this Messages is non-empty, then tcg_safe_infer is False.
+ -- It may be that tcg_safe_infer is False but this is empty, if no reasons
+ -- are supplied (#19714), or if those reasons have already been
+ -- reported by GHC.Driver.Main.markUnsafeInfer
+
tcg_tc_plugins :: [TcPluginSolver],
-- ^ A list of user-defined plugins for the constraint solver.
tcg_hf_plugins :: [HoleFitPlugin],
@@ -769,7 +779,7 @@ data TcLclEnv -- Changes as we move inside an expression
-- and for tidying types
tcl_lie :: TcRef WantedConstraints, -- Place to accumulate type constraints
- tcl_errs :: TcRef (Messages DiagnosticMessage) -- Place to accumulate errors
+ tcl_errs :: TcRef (Messages TcRnMessage) -- Place to accumulate diagnostics
}
setLclEnvTcLevel :: TcLclEnv -> TcLevel -> TcLclEnv
diff --git a/compiler/GHC/Tc/Utils/Backpack.hs b/compiler/GHC/Tc/Utils/Backpack.hs
index a1f802b254..a27c4de082 100644
--- a/compiler/GHC/Tc/Utils/Backpack.hs
+++ b/compiler/GHC/Tc/Utils/Backpack.hs
@@ -23,7 +23,6 @@ import GHC.Driver.Env
import GHC.Driver.Ppr
import GHC.Types.Basic (TypeOrKind(..))
-import GHC.Types.Error ( DiagnosticMessage )
import GHC.Types.Fixity (defaultFixity)
import GHC.Types.Fixity.Env
import GHC.Types.TypeEnv
@@ -372,7 +371,7 @@ checkUnit (VirtUnit indef) = do
-- an @hsig@ file.)
tcRnCheckUnit ::
HscEnv -> Unit ->
- IO (Messages DiagnosticMessage, Maybe ())
+ IO (Messages TcRnMessage, Maybe ())
tcRnCheckUnit hsc_env uid =
withTiming logger dflags
(text "Check unit id" <+> ppr uid)
@@ -393,7 +392,7 @@ tcRnCheckUnit hsc_env uid =
-- | Top-level driver for signature merging (run after typechecking
-- an @hsig@ file).
tcRnMergeSignatures :: HscEnv -> HsParsedModule -> TcGblEnv {- from local sig -} -> ModIface
- -> IO (Messages DiagnosticMessage, Maybe TcGblEnv)
+ -> IO (Messages TcRnMessage, Maybe TcGblEnv)
tcRnMergeSignatures hsc_env hpm orig_tcg_env iface =
withTiming logger dflags
(text "Signature merging" <+> brackets (ppr this_mod))
@@ -931,7 +930,7 @@ mergeSignatures
-- an @hsig@ file.)
tcRnInstantiateSignature ::
HscEnv -> Module -> RealSrcSpan ->
- IO (Messages DiagnosticMessage, Maybe TcGblEnv)
+ IO (Messages TcRnMessage, Maybe TcGblEnv)
tcRnInstantiateSignature hsc_env this_mod real_loc =
withTiming logger dflags
(text "Signature instantiation"<+>brackets (ppr this_mod))
diff --git a/compiler/GHC/Tc/Utils/Monad.hs b/compiler/GHC/Tc/Utils/Monad.hs
index 5568e34b75..3243be77de 100644
--- a/compiler/GHC/Tc/Utils/Monad.hs
+++ b/compiler/GHC/Tc/Utils/Monad.hs
@@ -76,7 +76,7 @@ module GHC.Tc.Utils.Monad(
tcCollectingUsage, tcScalingUsage, tcEmitBindingUsage,
-- * Shared error message stuff: renamer and typechecker
- mkLongErrAt, mkDecoratedSDocAt, addLongErrAt, reportDiagnostic, reportDiagnostics,
+ mkLongErrAt, mkTcRnMessage, addLongErrAt, reportDiagnostic, reportDiagnostics,
recoverM, mapAndRecoverM, mapAndReportM, foldAndRecoverM,
attemptM, tryTc,
askNoErrs, discardErrs, tryTcDiscardingErrs,
@@ -215,6 +215,7 @@ import qualified GHC.LanguageExtensions as LangExt
import Data.IORef
import Control.Monad
+import GHC.Tc.Errors.Types
import {-# SOURCE #-} GHC.Tc.Utils.Env ( tcInitTidyEnv )
import qualified Data.Map as Map
@@ -234,7 +235,7 @@ initTc :: HscEnv
-> Module
-> RealSrcSpan
-> TcM r
- -> IO (Messages DiagnosticMessage, Maybe r)
+ -> IO (Messages TcRnMessage, Maybe r)
-- Nothing => error thrown by the thing inside
-- (error messages should have been printed already)
@@ -243,7 +244,8 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this
used_gre_var <- newIORef [] ;
th_var <- newIORef False ;
th_splice_var<- newIORef False ;
- infer_var <- newIORef (True, emptyBag) ;
+ infer_var <- newIORef True ;
+ infer_reasons_var <- newIORef emptyMessages ;
dfun_n_var <- newIORef emptyOccSet ;
type_env_var <- case hsc_type_env_var hsc_env of {
Just (_mod, te_var) -> return te_var ;
@@ -341,7 +343,8 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this
tcg_hpc = False,
tcg_main = Nothing,
tcg_self_boot = NoSelfBoot,
- tcg_safeInfer = infer_var,
+ tcg_safe_infer = infer_var,
+ tcg_safe_infer_reasons = infer_reasons_var,
tcg_dependent_files = dependent_files_var,
tcg_tc_plugins = [],
tcg_hf_plugins = [],
@@ -362,7 +365,7 @@ initTcWithGbl :: HscEnv
-> TcGblEnv
-> RealSrcSpan
-> TcM r
- -> IO (Messages DiagnosticMessage, Maybe r)
+ -> IO (Messages TcRnMessage, Maybe r)
initTcWithGbl hsc_env gbl_env loc do_this
= do { lie_var <- newIORef emptyWC
; errs_var <- newIORef emptyMessages
@@ -408,7 +411,7 @@ initTcWithGbl hsc_env gbl_env loc do_this
; return (msgs, final_res)
}
-initTcInteractive :: HscEnv -> TcM a -> IO (Messages DiagnosticMessage, Maybe a)
+initTcInteractive :: HscEnv -> TcM a -> IO (Messages TcRnMessage, Maybe a)
-- Initialise the type checker monad for use in GHCi
initTcInteractive hsc_env thing_inside
= initTc hsc_env HsSrcFile False
@@ -968,10 +971,10 @@ wrapLocMA_ fn (L loc a) = setSrcSpan (locA loc) (fn a)
-- Reporting errors
-getErrsVar :: TcRn (TcRef (Messages DiagnosticMessage))
+getErrsVar :: TcRn (TcRef (Messages TcRnMessage))
getErrsVar = do { env <- getLclEnv; return (tcl_errs env) }
-setErrsVar :: TcRef (Messages DiagnosticMessage) -> TcRn a -> TcRn a
+setErrsVar :: TcRef (Messages TcRnMessage) -> TcRn a -> TcRn a
setErrsVar v = updLclEnv (\ env -> env { tcl_errs = v })
addErr :: SDoc -> TcRn ()
@@ -1001,7 +1004,7 @@ checkErr :: Bool -> SDoc -> TcRn ()
-- Add the error if the bool is False
checkErr ok msg = unless ok (addErr msg)
-addMessages :: Messages DiagnosticMessage -> TcRn ()
+addMessages :: Messages TcRnMessage -> TcRn ()
addMessages msgs1
= do { errs_var <- getErrsVar ;
msgs0 <- readTcRef errs_var ;
@@ -1030,40 +1033,42 @@ discardWarnings thing_inside
************************************************************************
-}
-mkLongErrAt :: SrcSpan -> SDoc -> SDoc -> TcRn (MsgEnvelope DiagnosticMessage)
+mkLongErrAt :: SrcSpan -> SDoc -> SDoc -> TcRn (MsgEnvelope TcRnMessage)
mkLongErrAt loc msg extra
= do { printer <- getPrintUnqualified ;
unit_state <- hsc_units <$> getTopEnv ;
- dflags <- getDynFlags ;
let msg' = pprWithUnitState unit_state msg in
- return $ mkLongMsgEnvelope dflags ErrorWithoutFlag loc printer msg' extra }
+ return $ mkErrorMsgEnvelope loc printer
+ $ TcRnUnknownMessage
+ $ mkDecoratedError [msg', extra] }
-mkDecoratedSDocAt :: DiagnosticReason
- -> SrcSpan
- -> SDoc
+mkTcRnMessage :: DiagnosticReason
+ -> SrcSpan
+ -> SDoc
-- ^ The important part of the message
- -> SDoc
+ -> SDoc
-- ^ The context of the message
- -> SDoc
+ -> SDoc
-- ^ Any supplementary information.
- -> TcRn (MsgEnvelope DiagnosticMessage)
-mkDecoratedSDocAt reason loc important context extra
+ -> TcRn (MsgEnvelope TcRnMessage)
+mkTcRnMessage reason loc important context extra
= do { printer <- getPrintUnqualified ;
unit_state <- hsc_units <$> getTopEnv ;
dflags <- getDynFlags ;
- let f = pprWithUnitState unit_state
- errDoc = [important, context, extra]
- errDoc' = DiagnosticMessage (mkDecorated $ map f errDoc) reason
+ let errDocs = map (pprWithUnitState unit_state)
+ [important, context, extra]
in
- return $ mkMsgEnvelope dflags loc printer errDoc' }
+ return $ mkMsgEnvelope dflags loc printer
+ $ TcRnUnknownMessage
+ $ mkDecoratedDiagnostic reason errDocs }
addLongErrAt :: SrcSpan -> SDoc -> SDoc -> TcRn ()
addLongErrAt loc msg extra = mkLongErrAt loc msg extra >>= reportDiagnostic
-reportDiagnostics :: [MsgEnvelope DiagnosticMessage] -> TcM ()
+reportDiagnostics :: [MsgEnvelope TcRnMessage] -> TcM ()
reportDiagnostics = mapM_ reportDiagnostic
-reportDiagnostic :: MsgEnvelope DiagnosticMessage -> TcRn ()
+reportDiagnostic :: MsgEnvelope TcRnMessage -> TcRn ()
reportDiagnostic msg
= do { traceTc "Adding diagnostic:" (pprLocMsgEnvelope msg) ;
errs_var <- getErrsVar ;
@@ -1241,7 +1246,7 @@ capture_constraints thing_inside
; lie <- readTcRef lie_var
; return (res, lie) }
-capture_messages :: TcM r -> TcM (r, Messages DiagnosticMessage)
+capture_messages :: TcM r -> TcM (r, Messages TcRnMessage)
-- capture_messages simply captures and returns the
-- errors arnd warnings generated by thing_inside
-- Precondition: thing_inside must not throw an exception!
@@ -1411,7 +1416,7 @@ foldAndRecoverM f acc (x:xs) =
Just acc' -> foldAndRecoverM f acc' xs }
-----------------------
-tryTc :: TcRn a -> TcRn (Maybe a, Messages DiagnosticMessage)
+tryTc :: TcRn a -> TcRn (Maybe a, Messages TcRnMessage)
-- (tryTc m) executes m, and returns
-- Just r, if m succeeds (returning r)
-- Nothing, if m fails
@@ -1561,9 +1566,9 @@ add_diagnostic_at :: DiagnosticReason -> SrcSpan -> SDoc -> SDoc -> TcRn ()
add_diagnostic_at reason loc msg extra_info
= do { printer <- getPrintUnqualified ;
dflags <- getDynFlags ;
- let { dia = mkLongMsgEnvelope dflags reason
- loc printer
- msg extra_info } ;
+ let { dia = mkMsgEnvelope dflags loc printer $
+ TcRnUnknownMessage $
+ mkDecoratedDiagnostic reason [msg, extra_info] } ;
reportDiagnostic dia }
@@ -1982,14 +1987,15 @@ addModFinalizersWithLclEnv mod_finalizers
-- | Mark that safe inference has failed
-- See Note [Safe Haskell Overlapping Instances Implementation]
-- although this is used for more than just that failure case.
-recordUnsafeInfer :: WarningMessages -> TcM ()
-recordUnsafeInfer warns =
- getGblEnv >>= \env -> writeTcRef (tcg_safeInfer env) (False, warns)
+recordUnsafeInfer :: Messages TcRnMessage -> TcM ()
+recordUnsafeInfer msgs =
+ getGblEnv >>= \env -> do writeTcRef (tcg_safe_infer env) False
+ writeTcRef (tcg_safe_infer_reasons env) msgs
-- | Figure out the final correct safe haskell mode
finalSafeMode :: DynFlags -> TcGblEnv -> IO SafeHaskellMode
finalSafeMode dflags tcg_env = do
- safeInf <- fst <$> readIORef (tcg_safeInfer tcg_env)
+ safeInf <- readIORef (tcg_safe_infer tcg_env)
return $ case safeHaskell dflags of
Sf_None | safeInferOn dflags && safeInf -> Sf_SafeInferred
| otherwise -> Sf_None
diff --git a/compiler/GHC/Tc/Validity.hs b/compiler/GHC/Tc/Validity.hs
index 91f1bcdbe7..a85158c122 100644
--- a/compiler/GHC/Tc/Validity.hs
+++ b/compiler/GHC/Tc/Validity.hs
@@ -68,7 +68,6 @@ import GHC.Types.SrcLoc
import GHC.Utils.Outputable as Outputable
import GHC.Utils.Panic
import GHC.Builtin.Uniques ( mkAlphaTyVarUnique )
-import GHC.Data.Bag ( emptyBag )
import qualified GHC.LanguageExtensions as LangExt
import Control.Monad
@@ -1565,7 +1564,7 @@ check_special_inst_head dflags is_boot is_sig ctxt clas cls_args
| clas_nm `elem` genericClassNames
, hand_written_bindings
= do { failIfTc (safeLanguageOn dflags) gen_inst_err
- ; when (safeInferOn dflags) (recordUnsafeInfer emptyBag) }
+ ; when (safeInferOn dflags) (recordUnsafeInfer emptyMessages) }
| clas_nm == hasFieldClassName
= checkHasFieldInst clas cls_args
diff --git a/compiler/GHC/Types/Error.hs b/compiler/GHC/Types/Error.hs
index e995ad8a4b..0ec3e8756c 100644
--- a/compiler/GHC/Types/Error.hs
+++ b/compiler/GHC/Types/Error.hs
@@ -1,20 +1,21 @@
-{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
module GHC.Types.Error
( -- * Messages
Messages
- , WarningMessages
- , ErrorMessages
, mkMessages
, getMessages
, emptyMessages
, isEmptyMessages
+ , singleMessage
, addMessage
, unionMessages
+ , unionManyMessages
, MsgEnvelope (..)
- , WarnMsg
-- * Classifying Messages
@@ -23,23 +24,31 @@ module GHC.Types.Error
, Diagnostic (..)
, DiagnosticMessage (..)
, DiagnosticReason (..)
+ , mkDiagnosticMessage
+ , mkPlainDiagnostic
+ , mkPlainError
+ , mkDecoratedDiagnostic
+ , mkDecoratedError
-- * Rendering Messages
, SDoc
, DecoratedSDoc (unDecorated)
+ , mkDecorated, mkSimpleDecorated
+
, pprMessageBag
- , mkDecorated
, mkLocMessage
, mkLocMessageAnn
, getCaretDiagnostic
-- * Queries
, isIntrinsicErrorMessage
+ , isExtrinsicErrorMessage
, isWarningMessage
, getErrorMessages
, getWarningMessages
, partitionMessages
, errorsFound
+ , errorsOrFatalWarningsFound
)
where
@@ -56,46 +65,62 @@ import GHC.Data.FastString (unpackFS)
import GHC.Data.StringBuffer (atLine, hGetStringBuffer, len, lexemeToString)
import GHC.Utils.Json
+import Data.Bifunctor
+import Data.Foldable ( fold )
+
{-
Note [Messages]
~~~~~~~~~~~~~~~
We represent the 'Messages' as a single bag of warnings and errors.
-The reason behind that is that there is a fluid relationship between errors and warnings and we want to
-be able to promote or demote errors and warnings based on certain flags (e.g. -Werror, -fdefer-type-errors
-or -XPartialTypeSignatures). More specifically, every diagnostic has a 'DiagnosticReason', but a warning
-'DiagnosticReason' might be associated with 'SevError', in the case of -Werror.
+The reason behind that is that there is a fluid relationship between errors
+and warnings and we want to be able to promote or demote errors and warnings
+based on certain flags (e.g. -Werror, -fdefer-type-errors or
+-XPartialTypeSignatures). More specifically, every diagnostic has a
+'DiagnosticReason', but a warning 'DiagnosticReason' might be associated with
+'SevError', in the case of -Werror.
We rely on the 'Severity' to distinguish between a warning and an error.
-'WarningMessages' and 'ErrorMessages' are for now simple type aliases to retain backward compatibility, but
-in future iterations these can be either parameterised over an 'e' message type (to make type signatures
-a bit more declarative) or removed altogether.
+'WarningMessages' and 'ErrorMessages' are for now simple type aliases to
+retain backward compatibility, but in future iterations these can be either
+parameterised over an 'e' message type (to make type signatures a bit more
+declarative) or removed altogether.
-}
--- | A collection of messages emitted by GHC during error reporting. A diagnostic message is typically
--- a warning or an error. See Note [Messages].
+-- | A collection of messages emitted by GHC during error reporting. A
+-- diagnostic message is typically a warning or an error. See Note [Messages].
+--
+-- /INVARIANT/: All the messages in this collection must be relevant, i.e.
+-- their 'Severity' should /not/ be 'SevIgnore'. The smart constructor
+-- 'mkMessages' will filter out any message which 'Severity' is 'SevIgnore'.
newtype Messages e = Messages { getMessages :: Bag (MsgEnvelope e) }
-
-instance Functor Messages where
- fmap f (Messages xs) = Messages (mapBag (fmap f) xs)
+ deriving newtype (Semigroup, Monoid)
+ deriving stock (Functor, Foldable, Traversable)
emptyMessages :: Messages e
emptyMessages = Messages emptyBag
mkMessages :: Bag (MsgEnvelope e) -> Messages e
-mkMessages = Messages
+mkMessages = Messages . filterBag interesting
+ where
+ interesting :: MsgEnvelope e -> Bool
+ interesting = (/=) SevIgnore . errMsgSeverity
isEmptyMessages :: Messages e -> Bool
isEmptyMessages (Messages msgs) = isEmptyBag msgs
+singleMessage :: MsgEnvelope e -> Messages e
+singleMessage e = addMessage e emptyMessages
+
{- Note [Discarding Messages]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Discarding a 'SevIgnore' message from 'addMessage' and 'unionMessages' is
-just an optimisation, as GHC would /also/ suppress any diagnostic which severity is
-'SevIgnore' before printing the message: See for example 'putLogMsg' and 'defaultLogAction'.
+Discarding a 'SevIgnore' message from 'addMessage' and 'unionMessages' is just
+an optimisation, as GHC would /also/ suppress any diagnostic which severity is
+'SevIgnore' before printing the message: See for example 'putLogMsg' and
+'defaultLogAction'.
-}
@@ -110,36 +135,38 @@ addMessage x (Messages xs)
-- See Note [Discarding Messages].
unionMessages :: Messages e -> Messages e -> Messages e
unionMessages (Messages msgs1) (Messages msgs2) =
- Messages (filterBag interesting $ msgs1 `unionBags` msgs2)
- where
- interesting :: MsgEnvelope e -> Bool
- interesting = (/=) SevIgnore . errMsgSeverity
+ Messages (msgs1 `unionBags` msgs2)
-type WarningMessages = Bag (MsgEnvelope DiagnosticMessage)
-type ErrorMessages = Bag (MsgEnvelope DiagnosticMessage)
+-- | Joins many 'Messages's together
+unionManyMessages :: Foldable f => f (Messages e) -> Messages e
+unionManyMessages = fold
-type WarnMsg = MsgEnvelope DiagnosticMessage
-
--- | A 'DecoratedSDoc' is isomorphic to a '[SDoc]' but it carries the invariant that the input '[SDoc]'
--- needs to be rendered /decorated/ into its final form, where the typical case would be adding bullets
--- between each elements of the list.
--- The type of decoration depends on the formatting function used, but in practice GHC uses the
--- 'formatBulleted'.
+-- | A 'DecoratedSDoc' is isomorphic to a '[SDoc]' but it carries the
+-- invariant that the input '[SDoc]' needs to be rendered /decorated/ into its
+-- final form, where the typical case would be adding bullets between each
+-- elements of the list. The type of decoration depends on the formatting
+-- function used, but in practice GHC uses the 'formatBulleted'.
newtype DecoratedSDoc = Decorated { unDecorated :: [SDoc] }
-- | Creates a new 'DecoratedSDoc' out of a list of 'SDoc'.
mkDecorated :: [SDoc] -> DecoratedSDoc
mkDecorated = Decorated
+-- | Creates a new 'DecoratedSDoc' out of a single 'SDoc'
+mkSimpleDecorated :: SDoc -> DecoratedSDoc
+mkSimpleDecorated doc = Decorated [doc]
+
{-
Note [Rendering Messages]
~~~~~~~~~~~~~~~~~~~~~~~~~
-Turning 'Messages' into something that renders nicely for the user is one of the last steps, and it
-happens typically at the application's boundaries (i.e. from the 'Driver' upwards).
+Turning 'Messages' into something that renders nicely for the user is one of
+the last steps, and it happens typically at the application's boundaries (i.e.
+from the 'Driver' upwards).
-For now (see #18516) this class has few instance, but the idea is that as
-the more domain-specific types are defined, the more instances we would get. For example, given something like:
+For now (see #18516) this class has few instance, but the idea is that as the
+more domain-specific types are defined, the more instances we would get. For
+example, given something like:
data TcRnDiagnostic
= TcRnOutOfScope ..
@@ -147,36 +174,40 @@ the more domain-specific types are defined, the more instances we would get. For
newtype TcRnMessage = TcRnMessage (DiagnosticMessage TcRnDiagnostic)
-We could then define how a 'TcRnDiagnostic' is displayed to the user. Rather than scattering pieces of
-'SDoc' around the codebase, we would write once for all:
+We could then define how a 'TcRnDiagnostic' is displayed to the user. Rather
+than scattering pieces of 'SDoc' around the codebase, we would write once for
+all:
instance Diagnostic TcRnDiagnostic where
diagnosticMessage (TcRnMessage msg) = case diagMessage msg of
TcRnOutOfScope .. -> Decorated [text "Out of scope error ..."]
...
-This way, we can easily write generic rendering functions for errors that all they care about is the
-knowledge that a given type 'e' has a 'Diagnostic' constraint.
+This way, we can easily write generic rendering functions for errors that all
+they care about is the knowledge that a given type 'e' has a 'Diagnostic'
+constraint.
-}
-- | A class identifying a diagnostic.
-- Dictionary.com defines a diagnostic as:
--
--- \"a message output by a computer diagnosing an error in a computer program, computer system,
--- or component device\".
+-- \"a message output by a computer diagnosing an error in a computer program,
+-- computer system, or component device\".
--
--- A 'Diagnostic' carries the /actual/ description of the message (which, in GHC's case, it can be
--- an error or a warning) and the /reason/ why such message was generated in the first place.
--- See also Note [Rendering Messages].
+-- A 'Diagnostic' carries the /actual/ description of the message (which, in
+-- GHC's case, it can be an error or a warning) and the /reason/ why such
+-- message was generated in the first place. See also Note [Rendering
+-- Messages].
class Diagnostic a where
diagnosticMessage :: a -> DecoratedSDoc
diagnosticReason :: a -> DiagnosticReason
--- | A generic 'Diagnostic' message, without any further classification or provenance:
--- By looking at a 'DiagnosticMessage' we don't know neither /where/ it was generated nor how to
--- intepret its payload (as it's just a structured document). All we can do is to print it out and
--- look at its 'DiagnosticReason'.
+-- | A generic 'Diagnostic' message, without any further classification or
+-- provenance: By looking at a 'DiagnosticMessage' we don't know neither
+-- /where/ it was generated nor how to intepret its payload (as it's just a
+-- structured document). All we can do is to print it out and look at its
+-- 'DiagnosticReason'.
data DiagnosticMessage = DiagnosticMessage
{ diagMessage :: !DecoratedSDoc
, diagReason :: !DiagnosticReason
@@ -186,13 +217,34 @@ instance Diagnostic DiagnosticMessage where
diagnosticMessage = diagMessage
diagnosticReason = diagReason
--- | The reason /why/ a 'Diagnostic' was emitted in the first place. Diagnostic messages
--- are born within GHC with a very precise reason, which can be completely statically-computed
--- (i.e. this is an error or a warning no matter what), or influenced by the specific state
--- of the 'DynFlags' at the moment of the creation of a new 'Diagnostic'. For example, a parsing
--- error is /always/ going to be an error, whereas a 'WarningWithoutFlag Opt_WarnUnusedImports' might turn
--- into an error due to '-Werror' or '-Werror=warn-unused-imports'. Interpreting a 'DiagnosticReason'
--- together with its associated 'Severity' gives us the full picture.
+-- | Create a 'DiagnosticMessage' with a 'DiagnosticReason'
+mkDiagnosticMessage :: DecoratedSDoc -> DiagnosticReason -> DiagnosticMessage
+mkDiagnosticMessage = DiagnosticMessage
+
+mkPlainDiagnostic :: DiagnosticReason -> SDoc -> DiagnosticMessage
+mkPlainDiagnostic rea doc = DiagnosticMessage (mkSimpleDecorated doc) rea
+
+-- | Create an error 'DiagnosticMessage' holding just a single 'SDoc'
+mkPlainError :: SDoc -> DiagnosticMessage
+mkPlainError doc = DiagnosticMessage (mkSimpleDecorated doc) ErrorWithoutFlag
+
+-- | Create a 'DiagnosticMessage' from a list of bulleted SDocs and a 'DiagnosticReason'
+mkDecoratedDiagnostic :: DiagnosticReason -> [SDoc] -> DiagnosticMessage
+mkDecoratedDiagnostic rea docs = DiagnosticMessage (mkDecorated docs) rea
+
+-- | Create an error 'DiagnosticMessage' from a list of bulleted SDocs
+mkDecoratedError :: [SDoc] -> DiagnosticMessage
+mkDecoratedError docs = DiagnosticMessage (mkDecorated docs) ErrorWithoutFlag
+
+-- | The reason /why/ a 'Diagnostic' was emitted in the first place.
+-- Diagnostic messages are born within GHC with a very precise reason, which
+-- can be completely statically-computed (i.e. this is an error or a warning
+-- no matter what), or influenced by the specific state of the 'DynFlags' at
+-- the moment of the creation of a new 'Diagnostic'. For example, a parsing
+-- error is /always/ going to be an error, whereas a 'WarningWithoutFlag
+-- Opt_WarnUnusedImports' might turn into an error due to '-Werror' or
+-- '-Werror=warn-unused-imports'. Interpreting a 'DiagnosticReason' together
+-- with its associated 'Severity' gives us the full picture.
data DiagnosticReason
= WarningWithoutFlag
-- ^ Born as a warning.
@@ -211,19 +263,22 @@ instance Outputable DiagnosticReason where
-- | An envelope for GHC's facts about a running program, parameterised over the
-- /domain-specific/ (i.e. parsing, typecheck-renaming, etc) diagnostics.
--
--- To say things differently, GHC emits /diagnostics/ about the running program, each of which is wrapped
--- into a 'MsgEnvelope' that carries specific information like where the error happened, etc.
--- Finally, multiple 'MsgEnvelope's are aggregated into 'Messages' that are returned to the user.
+-- To say things differently, GHC emits /diagnostics/ about the running
+-- program, each of which is wrapped into a 'MsgEnvelope' that carries
+-- specific information like where the error happened, etc. Finally, multiple
+-- 'MsgEnvelope's are aggregated into 'Messages' that are returned to the
+-- user.
data MsgEnvelope e = MsgEnvelope
{ errMsgSpan :: SrcSpan
-- ^ The SrcSpan is used for sorting errors into line-number order
, errMsgContext :: PrintUnqualified
, errMsgDiagnostic :: e
, errMsgSeverity :: Severity
- } deriving Functor
+ } deriving (Functor, Foldable, Traversable)
--- | The class for a diagnostic message. The main purpose is to classify a message within GHC,
--- to distinguish it from a debug/dump message vs a proper diagnostic, for which we include a 'DiagnosticReason'.
+-- | The class for a diagnostic message. The main purpose is to classify a
+-- message within GHC, to distinguish it from a debug/dump message vs a proper
+-- diagnostic, for which we include a 'DiagnosticReason'.
data MessageClass
= MCOutput
| MCFatal
@@ -238,33 +293,37 @@ data MessageClass
-- No file\/line\/column stuff.
| MCDiagnostic Severity DiagnosticReason
- -- ^ Diagnostics from the compiler. This constructor
- -- is very powerful as it allows the construction
- -- of a 'MessageClass' with a completely arbitrary
- -- permutation of 'Severity' and 'DiagnosticReason'. As such,
- -- users are encouraged to use the 'mkMCDiagnostic' smart constructor instead.
- -- Use this constructor directly only if you need to construct and manipulate diagnostic
- -- messages directly, for example inside 'GHC.Utils.Error'. In all the other circumstances,
- -- /especially/ when emitting compiler diagnostics, use the smart constructor.
+ -- ^ Diagnostics from the compiler. This constructor is very powerful as
+ -- it allows the construction of a 'MessageClass' with a completely
+ -- arbitrary permutation of 'Severity' and 'DiagnosticReason'. As such,
+ -- users are encouraged to use the 'mkMCDiagnostic' smart constructor
+ -- instead. Use this constructor directly only if you need to construct
+ -- and manipulate diagnostic messages directly, for example inside
+ -- 'GHC.Utils.Error'. In all the other circumstances, /especially/ when
+ -- emitting compiler diagnostics, use the smart constructor.
deriving (Eq, Show)
{- Note [Suppressing Messages]
-The 'SevIgnore' constructor is used to generate messages for diagnostics which are
-meant to be suppressed and not reported to the user: the classic example are warnings
-for which the user didn't enable the corresponding 'WarningFlag', so GHC shouldn't print them.
+The 'SevIgnore' constructor is used to generate messages for diagnostics which
+are meant to be suppressed and not reported to the user: the classic example
+are warnings for which the user didn't enable the corresponding 'WarningFlag',
+so GHC shouldn't print them.
-A different approach would be to extend the zoo of 'mkMsgEnvelope' functions to return
-a 'Maybe (MsgEnvelope e)', so that we won't need to even create the message to begin with.
-Both approaches have been evaluated, but we settled on the "SevIgnore one" for a number of reasons:
+A different approach would be to extend the zoo of 'mkMsgEnvelope' functions
+to return a 'Maybe (MsgEnvelope e)', so that we won't need to even create the
+message to begin with. Both approaches have been evaluated, but we settled on
+the "SevIgnore one" for a number of reasons:
* It's less invasive to deal with;
-* It plays slightly better with deferred diagnostics (see 'GHC.Tc.Errors') as for those we need
- to be able to /always/ produce a message (so that is reported at runtime);
-* It gives us more freedom: we can still decide to drop a 'SevIgnore' message at leisure, or we can
- decide to keep it around until the last moment. Maybe in the future we would need to
- turn a 'SevIgnore' into something else, for example to "unsuppress" diagnostics if a flag is
- set: with this approach, we have more leeway to accommodate new features.
+* It plays slightly better with deferred diagnostics (see 'GHC.Tc.Errors') as
+ for those we need to be able to /always/ produce a message (so that is
+ reported at runtime);
+* It gives us more freedom: we can still decide to drop a 'SevIgnore' message
+ at leisure, or we can decide to keep it around until the last moment. Maybe
+ in the future we would need to turn a 'SevIgnore' into something else, for
+ example to "unsuppress" diagnostics if a flag is set: with this approach, we
+ have more leeway to accommodate new features.
-}
@@ -446,36 +505,49 @@ getCaretDiagnostic msg_class (RealSrcSpan span _) =
{- Note [Intrinsic And Extrinsic Failures]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We distinguish between /intrinsic/ and /extrinsic/ failures. We classify in the former category
-those diagnostics which are /essentially/ failures, and their nature can't be changed. This is
-the case for 'ErrorWithoutFlag'. We classify as /extrinsic/ all those diagnostics (like fatal warnings)
-which are born as warnings but which are still failures under particular 'DynFlags' settings. It's important
-to be aware of such logic distinction, because when we are inside the typechecker or the desugarer, we are
-interested about intrinsic errors, and to bail out as soon as we find one of them. Conversely, if we find
-an /extrinsic/ one, for example because a particular 'WarningFlag' makes a warning and error, we /don't/
-want to bail out, that's still not the right time to do so: Rather, we want to first collect all the
-diagnostics, and later classify and report them appropriately (in the driver).
-
+We distinguish between /intrinsic/ and /extrinsic/ failures. We classify in
+the former category those diagnostics which are /essentially/ failures, and
+their nature can't be changed. This is the case for 'ErrorWithoutFlag'. We
+classify as /extrinsic/ all those diagnostics (like fatal warnings) which are
+born as warnings but which are still failures under particular 'DynFlags'
+settings. It's important to be aware of such logic distinction, because when
+we are inside the typechecker or the desugarer, we are interested about
+intrinsic errors, and to bail out as soon as we find one of them. Conversely,
+if we find an /extrinsic/ one, for example because a particular 'WarningFlag'
+makes a warning and error, we /don't/ want to bail out, that's still not the
+right time to do so: Rather, we want to first collect all the diagnostics, and
+later classify and report them appropriately (in the driver).
-}
-
--- | Returns 'True' if this is, intrinsically, a failure. See Note [Intrinsic And Extrinsic Failures].
+-- | Returns 'True' if this is, intrinsically, a failure. See
+-- Note [Intrinsic And Extrinsic Failures].
isIntrinsicErrorMessage :: Diagnostic e => MsgEnvelope e -> Bool
isIntrinsicErrorMessage = (==) ErrorWithoutFlag . diagnosticReason . errMsgDiagnostic
isWarningMessage :: Diagnostic e => MsgEnvelope e -> Bool
isWarningMessage = not . isIntrinsicErrorMessage
+-- | Are there any hard errors here? -Werror warnings are /not/ detected. If
+-- you want to check for -Werror warnings, use 'errorsOrFatalWarningsFound'.
errorsFound :: Diagnostic e => Messages e -> Bool
errorsFound (Messages msgs) = any isIntrinsicErrorMessage msgs
+-- | Returns 'True' if the envelope contains a message that will stop
+-- compilation: either an intrinsic error or a fatal (-Werror) warning
+isExtrinsicErrorMessage :: MsgEnvelope e -> Bool
+isExtrinsicErrorMessage = (==) SevError . errMsgSeverity
+
+-- | Are there any errors or -Werror warnings here?
+errorsOrFatalWarningsFound :: Messages e -> Bool
+errorsOrFatalWarningsFound (Messages msgs) = any isExtrinsicErrorMessage msgs
+
getWarningMessages :: Diagnostic e => Messages e -> Bag (MsgEnvelope e)
getWarningMessages (Messages xs) = fst $ partitionBag isWarningMessage xs
getErrorMessages :: Diagnostic e => Messages e -> Bag (MsgEnvelope e)
getErrorMessages (Messages xs) = fst $ partitionBag isIntrinsicErrorMessage xs
--- | Partitions the 'Messages' and returns a tuple which first element are the warnings, and the
--- second the errors.
-partitionMessages :: Diagnostic e => Messages e -> (Bag (MsgEnvelope e), Bag (MsgEnvelope e))
-partitionMessages (Messages xs) = partitionBag isWarningMessage xs
+-- | Partitions the 'Messages' and returns a tuple which first element are the
+-- warnings, and the second the errors.
+partitionMessages :: Diagnostic e => Messages e -> (Messages e, Messages e)
+partitionMessages (Messages xs) = bimap Messages Messages (partitionBag isWarningMessage xs)
diff --git a/compiler/GHC/Types/SourceError.hs b/compiler/GHC/Types/SourceError.hs
index b8a1e932e0..4979d9188b 100644
--- a/compiler/GHC/Types/SourceError.hs
+++ b/compiler/GHC/Types/SourceError.hs
@@ -10,25 +10,29 @@ module GHC.Types.SourceError
where
import GHC.Prelude
-import GHC.Data.Bag
import GHC.Types.Error
import GHC.Utils.Monad
import GHC.Utils.Panic
import GHC.Utils.Exception
+import GHC.Utils.Error (pprMsgEnvelopeBagWithLoc)
+import GHC.Utils.Outputable
+
+import GHC.Driver.Errors.Ppr () -- instance Diagnostic GhcMessage
+import GHC.Driver.Errors.Types
import Control.Monad.Catch as MC (MonadCatch, catch)
-mkSrcErr :: ErrorMessages -> SourceError
+mkSrcErr :: Messages GhcMessage -> SourceError
mkSrcErr = SourceError
-srcErrorMessages :: SourceError -> ErrorMessages
+srcErrorMessages :: SourceError -> Messages GhcMessage
srcErrorMessages (SourceError msgs) = msgs
-throwErrors :: MonadIO io => ErrorMessages -> io a
+throwErrors :: MonadIO io => Messages GhcMessage -> io a
throwErrors = liftIO . throwIO . mkSrcErr
-throwOneError :: MonadIO io => MsgEnvelope DiagnosticMessage -> io a
-throwOneError = throwErrors . unitBag
+throwOneError :: MonadIO io => MsgEnvelope GhcMessage -> io a
+throwOneError = throwErrors . singleMessage
-- | A source error is an error that is caused by one or more errors in the
-- source code. A 'SourceError' is thrown by many functions in the
@@ -46,10 +50,18 @@ throwOneError = throwErrors . unitBag
--
-- See 'printExceptionAndWarnings' for more information on what to take care
-- of when writing a custom error handler.
-newtype SourceError = SourceError ErrorMessages
+newtype SourceError = SourceError (Messages GhcMessage)
instance Show SourceError where
- show (SourceError msgs) = unlines . map show . bagToList $ msgs
+ -- We implement 'Show' because it's required by the 'Exception' instance, but diagnostics
+ -- shouldn't be shown via the 'Show' typeclass, but rather rendered using the ppr functions.
+ -- This also explains why there is no 'Show' instance for a 'MsgEnvelope'.
+ show (SourceError msgs) =
+ renderWithContext defaultSDocContext
+ . vcat
+ . pprMsgEnvelopeBagWithLoc
+ . getMessages
+ $ msgs
instance Exception SourceError
diff --git a/compiler/GHC/Utils/Error.hs b/compiler/GHC/Utils/Error.hs
index 99bff97a5b..7e614588f6 100644
--- a/compiler/GHC/Utils/Error.hs
+++ b/compiler/GHC/Utils/Error.hs
@@ -15,13 +15,13 @@ module GHC.Utils.Error (
Severity(..),
-- * Messages
- WarnMsg,
+ Diagnostic(..),
MsgEnvelope(..),
MessageClass(..),
SDoc,
DecoratedSDoc(unDecorated),
- Messages, ErrorMessages, WarningMessages,
- unionMessages,
+ Messages,
+ mkMessages, unionMessages,
errorsFound, isEmptyMessages,
-- ** Formatting
@@ -33,9 +33,14 @@ module GHC.Utils.Error (
-- ** Construction
emptyMessages, mkDecorated, mkLocMessage, mkLocMessageAnn,
mkMsgEnvelope, mkPlainMsgEnvelope, mkPlainErrorMsgEnvelope,
- mkShortMsgEnvelope, mkShortErrorMsgEnvelope, mkLongMsgEnvelope,
+ mkErrorMsgEnvelope,
mkMCDiagnostic, errorDiagnostic, diagReasonSeverity,
+ mkPlainError,
+ mkPlainDiagnostic,
+ mkDecoratedError,
+ mkDecoratedDiagnostic,
+
-- * Utilities
doIfSet, doIfSet_dyn,
getCaretDiagnostic,
@@ -97,6 +102,15 @@ diagReasonSeverity _ ErrorWithoutFlag
+-- | Make a 'MessageClass' for a given 'DiagnosticReason', consulting the 'DynFlags'.
+mkMCDiagnostic :: DynFlags -> DiagnosticReason -> MessageClass
+mkMCDiagnostic dflags reason = MCDiagnostic (diagReasonSeverity dflags reason) reason
+
+-- | Varation of 'mkMCDiagnostic' which can be used when we are /sure/ the
+-- input 'DiagnosticReason' /is/ 'ErrorWithoutFlag'.
+errorDiagnostic :: MessageClass
+errorDiagnostic = MCDiagnostic SevError ErrorWithoutFlag
+
--
-- Creating MsgEnvelope(s)
--
@@ -115,6 +129,9 @@ mk_msg_envelope severity locn print_unqual err
, errMsgSeverity = severity
}
+-- | Wrap a 'Diagnostic' in a 'MsgEnvelope', recording its location.
+-- If you know your 'Diagnostic' is an error, consider using 'mkErrorMsgEnvelope',
+-- which does not require looking at the 'DynFlags'
mkMsgEnvelope
:: Diagnostic e
=> DynFlags
@@ -125,63 +142,34 @@ mkMsgEnvelope
mkMsgEnvelope dflags locn print_unqual err
= mk_msg_envelope (diagReasonSeverity dflags (diagnosticReason err)) locn print_unqual err
--- | Make a 'MessageClass' for a given 'DiagnosticReason', consulting the 'DynFlags'.
-mkMCDiagnostic :: DynFlags -> DiagnosticReason -> MessageClass
-mkMCDiagnostic dflags reason = MCDiagnostic (diagReasonSeverity dflags reason) reason
-
--- | Varation of 'mkMCDiagnostic' which can be used when we are /sure/ the
--- input 'DiagnosticReason' /is/ 'ErrorWithoutFlag'.
-errorDiagnostic :: MessageClass
-errorDiagnostic = MCDiagnostic SevError ErrorWithoutFlag
-
--- | A long (multi-line) diagnostic message.
--- The 'Severity' will be calculated out of the 'DiagnosticReason', and will likely be
--- incorrect in the presence of '-Werror'.
-mkLongMsgEnvelope :: DynFlags
- -> DiagnosticReason
- -> SrcSpan
- -> PrintUnqualified
- -> SDoc
- -> SDoc
- -> MsgEnvelope DiagnosticMessage
-mkLongMsgEnvelope dflags rea locn unqual msg extra =
- mkMsgEnvelope dflags locn unqual (DiagnosticMessage (mkDecorated [msg,extra]) rea)
-
--- | A short (one-line) diagnostic message.
--- Same 'Severity' considerations as for 'mkLongMsgEnvelope'.
-mkShortMsgEnvelope :: DynFlags
- -> DiagnosticReason
- -> SrcSpan
+-- | Wrap a 'Diagnostic' in a 'MsgEnvelope', recording its location.
+-- Precondition: the diagnostic is, in fact, an error. That is,
+-- @diagnosticReason msg == ErrorWithoutFlag@.
+mkErrorMsgEnvelope :: Diagnostic e
+ => SrcSpan
-> PrintUnqualified
- -> SDoc
- -> MsgEnvelope DiagnosticMessage
-mkShortMsgEnvelope dflags rea locn unqual msg =
- mkMsgEnvelope dflags locn unqual (DiagnosticMessage (mkDecorated [msg]) rea)
-
-mkShortErrorMsgEnvelope :: SrcSpan
- -> PrintUnqualified
- -> SDoc
- -> MsgEnvelope DiagnosticMessage
-mkShortErrorMsgEnvelope locn unqual msg =
- mk_msg_envelope SevError locn unqual (DiagnosticMessage (mkDecorated [msg]) ErrorWithoutFlag)
+ -> e
+ -> MsgEnvelope e
+mkErrorMsgEnvelope locn unqual msg =
+ mk_msg_envelope SevError locn unqual msg
-- | Variant that doesn't care about qualified/unqualified names.
--- Same 'Severity' considerations as for 'mkLongMsgEnvelope'.
-mkPlainMsgEnvelope :: DynFlags
- -> DiagnosticReason
+mkPlainMsgEnvelope :: Diagnostic e
+ => DynFlags
-> SrcSpan
- -> SDoc
- -> MsgEnvelope DiagnosticMessage
-mkPlainMsgEnvelope dflags rea locn msg =
- mkMsgEnvelope dflags locn alwaysQualify (DiagnosticMessage (mkDecorated [msg]) rea)
+ -> e
+ -> MsgEnvelope e
+mkPlainMsgEnvelope dflags locn msg =
+ mkMsgEnvelope dflags locn alwaysQualify msg
-- | Variant of 'mkPlainMsgEnvelope' which can be used when we are /sure/ we
-- are constructing a diagnostic with a 'ErrorWithoutFlag' reason.
-mkPlainErrorMsgEnvelope :: SrcSpan
- -> SDoc
- -> MsgEnvelope DiagnosticMessage
+mkPlainErrorMsgEnvelope :: Diagnostic e
+ => SrcSpan
+ -> e
+ -> MsgEnvelope e
mkPlainErrorMsgEnvelope locn msg =
- mk_msg_envelope SevError locn alwaysQualify (DiagnosticMessage (mkDecorated [msg]) ErrorWithoutFlag)
+ mk_msg_envelope SevError locn alwaysQualify msg
-------------------------
data Validity
@@ -582,5 +570,3 @@ of the execution through the various labels) and ghc.totals.txt (total time
spent in each label).
-}
-
-
diff --git a/compiler/GHC/Utils/Misc.hs b/compiler/GHC/Utils/Misc.hs
index c977f89078..67d3f11c67 100644
--- a/compiler/GHC/Utils/Misc.hs
+++ b/compiler/GHC/Utils/Misc.hs
@@ -91,9 +91,6 @@ module GHC.Utils.Misc (
looksLikeModuleName,
looksLikePackageName,
- -- * Argument processing
- getCmd, toCmdArgs, toArgs,
-
-- * Integers
exactLog2,
@@ -1102,67 +1099,6 @@ looksLikeModuleName (c:cs) = isUpper c && go cs
looksLikePackageName :: String -> Bool
looksLikePackageName = all (all isAlphaNum <&&> not . (all isDigit)) . split '-'
-{-
-Akin to @Prelude.words@, but acts like the Bourne shell, treating
-quoted strings as Haskell Strings, and also parses Haskell [String]
-syntax.
--}
-
-getCmd :: String -> Either String -- Error
- (String, String) -- (Cmd, Rest)
-getCmd s = case break isSpace $ dropWhile isSpace s of
- ([], _) -> Left ("Couldn't find command in " ++ show s)
- res -> Right res
-
-toCmdArgs :: String -> Either String -- Error
- (String, [String]) -- (Cmd, Args)
-toCmdArgs s = case getCmd s of
- Left err -> Left err
- Right (cmd, s') -> case toArgs s' of
- Left err -> Left err
- Right args -> Right (cmd, args)
-
-toArgs :: String -> Either String -- Error
- [String] -- Args
-toArgs str
- = case dropWhile isSpace str of
- s@('[':_) -> case reads s of
- [(args, spaces)]
- | all isSpace spaces ->
- Right args
- _ ->
- Left ("Couldn't read " ++ show str ++ " as [String]")
- s -> toArgs' s
- where
- toArgs' :: String -> Either String [String]
- -- Remove outer quotes:
- -- > toArgs' "\"foo\" \"bar baz\""
- -- Right ["foo", "bar baz"]
- --
- -- Keep inner quotes:
- -- > toArgs' "-DFOO=\"bar baz\""
- -- Right ["-DFOO=\"bar baz\""]
- toArgs' s = case dropWhile isSpace s of
- [] -> Right []
- ('"' : _) -> do
- -- readAsString removes outer quotes
- (arg, rest) <- readAsString s
- (arg:) `fmap` toArgs' rest
- s' -> case break (isSpace <||> (== '"')) s' of
- (argPart1, s''@('"':_)) -> do
- (argPart2, rest) <- readAsString s''
- -- show argPart2 to keep inner quotes
- ((argPart1 ++ show argPart2):) `fmap` toArgs' rest
- (arg, s'') -> (arg:) `fmap` toArgs' s''
-
- readAsString :: String -> Either String (String, String)
- readAsString s = case reads s of
- [(arg, rest)]
- -- rest must either be [] or start with a space
- | all isSpace (take 1 rest) ->
- Right (arg, rest)
- _ ->
- Left ("Couldn't read " ++ show s ++ " as String")
-----------------------------------------------------------------------------
-- Integers
diff --git a/compiler/ghc.cabal.in b/compiler/ghc.cabal.in
index f260600ba5..15018529d3 100644
--- a/compiler/ghc.cabal.in
+++ b/compiler/ghc.cabal.in
@@ -384,6 +384,8 @@ Library
GHC.Driver.Env
GHC.Driver.Env.Types
GHC.Driver.Errors
+ GHC.Driver.Errors.Ppr
+ GHC.Driver.Errors.Types
GHC.Driver.Flags
GHC.Driver.Hooks
GHC.Driver.Main
@@ -413,6 +415,8 @@ Library
GHC.HsToCore.Binds
GHC.HsToCore.Coverage
GHC.HsToCore.Docs
+ GHC.HsToCore.Errors.Ppr
+ GHC.HsToCore.Errors.Types
GHC.HsToCore.Expr
GHC.HsToCore.Foreign.Call
GHC.HsToCore.Foreign.Decl
@@ -475,6 +479,7 @@ Library
GHC.Parser.CharClass
GHC.Parser.Errors
GHC.Parser.Errors.Ppr
+ GHC.Parser.Errors.Types
GHC.Parser.Header
GHC.Parser.Lexer
GHC.Parser.PostProcess
@@ -574,6 +579,8 @@ Library
GHC.Tc.Errors
GHC.Tc.Errors.Hole
GHC.Tc.Errors.Hole.FitTypes
+ GHC.Tc.Errors.Ppr
+ GHC.Tc.Errors.Types
GHC.Tc.Gen.Annotation
GHC.Tc.Gen.App
GHC.Tc.Gen.Arrow
diff --git a/ghc/GHCi/UI.hs b/ghc/GHCi/UI.hs
index c53f6771b5..4f126b92b3 100644
--- a/ghc/GHCi/UI.hs
+++ b/ghc/GHCi/UI.hs
@@ -77,6 +77,7 @@ import GHC.Builtin.Types( stringTyCon_RDR )
import GHC.Types.Name.Reader as RdrName ( getGRE_NameQualifier_maybes, getRdrName )
import GHC.Types.SrcLoc as SrcLoc
import qualified GHC.Parser.Lexer as Lexer
+import GHC.Parser.Header ( toArgs )
import GHC.Unit
import GHC.Unit.State
@@ -293,7 +294,7 @@ keepGoing' a str = a str >> return False
keepGoingPaths :: ([FilePath] -> InputT GHCi ()) -> (String -> InputT GHCi Bool)
keepGoingPaths a str
- = do case toArgs str of
+ = do case toArgsNoLoc str of
Left err -> liftIO $ hPutStrLn stderr err
Right args -> a args
return False
@@ -1562,7 +1563,7 @@ pprInfo (thing, fixity, cls_insts, fam_insts, docs)
-- :main
runMain :: GhciMonad m => String -> m ()
-runMain s = case toArgs s of
+runMain s = case toArgsNoLoc s of
Left err -> liftIO (hPutStrLn stderr err)
Right args ->
do dflags <- getDynFlags
@@ -1583,6 +1584,33 @@ doWithArgs :: GhciMonad m => [String] -> String -> m ()
doWithArgs args cmd = enqueueCommands ["System.Environment.withArgs " ++
show args ++ " (" ++ cmd ++ ")"]
+{-
+Akin to @Prelude.words@, but acts like the Bourne shell, treating
+quoted strings as Haskell Strings, and also parses Haskell [String]
+syntax.
+-}
+
+getCmd :: String -> Either String -- Error
+ (String, String) -- (Cmd, Rest)
+getCmd s = case break isSpace $ dropWhile isSpace s of
+ ([], _) -> Left ("Couldn't find command in " ++ show s)
+ res -> Right res
+
+toCmdArgs :: String -> Either String -- Error
+ (String, [String]) -- (Cmd, Args)
+toCmdArgs s = case getCmd s of
+ Left err -> Left err
+ Right (cmd, s') -> case toArgsNoLoc s' of
+ Left err -> Left err
+ Right args -> Right (cmd, args)
+
+-- wrapper around GHC.Parser.Header.toArgs, but without locations
+toArgsNoLoc :: String -> Either String [String]
+toArgsNoLoc str = map unLoc <$> toArgs fake_loc str
+ where
+ fake_loc = mkRealSrcLoc (fsLit "<interactive>") 1 1
+ -- this should never be seen, because it's discarded with the `map unLoc`
+
-----------------------------------------------------------------------------
-- :cd
@@ -2854,11 +2882,11 @@ setCmd "-a" = showOptions True
setCmd str
= case getCmd str of
Right ("args", rest) ->
- case toArgs rest of
+ case toArgsNoLoc rest of
Left err -> liftIO (hPutStrLn stderr err)
Right args -> setArgs args
Right ("prog", rest) ->
- case toArgs rest of
+ case toArgsNoLoc rest of
Right [prog] -> setProg prog
_ -> liftIO (hPutStrLn stderr "syntax: :set prog <progname>")
@@ -2877,7 +2905,7 @@ setCmd str
Right ("stop", rest) -> setStop $ dropWhile isSpace rest
Right ("local-config", rest) ->
setLocalConfigBehaviour $ dropWhile isSpace rest
- _ -> case toArgs str of
+ _ -> case toArgsNoLoc str of
Left err -> liftIO (hPutStrLn stderr err)
Right wds -> setOptions wds
@@ -2885,7 +2913,7 @@ setiCmd :: GhciMonad m => String -> m ()
setiCmd "" = GHC.getInteractiveDynFlags >>= liftIO . showDynFlags False
setiCmd "-a" = GHC.getInteractiveDynFlags >>= liftIO . showDynFlags True
setiCmd str =
- case toArgs str of
+ case toArgsNoLoc str of
Left err -> liftIO (hPutStrLn stderr err)
Right wds -> newDynFlags True wds
diff --git a/testsuite/tests/driver/StringListOptions.hs b/testsuite/tests/driver/StringListOptions.hs
new file mode 100644
index 0000000000..51f8b6db4d
--- /dev/null
+++ b/testsuite/tests/driver/StringListOptions.hs
@@ -0,0 +1,3 @@
+{-# OPTIONS_GHC [ ] #-}
+
+module StringListOptions where
diff --git a/testsuite/tests/driver/T2464.stderr b/testsuite/tests/driver/T2464.stderr
index 3d0074fc82..bba952fd30 100644
--- a/testsuite/tests/driver/T2464.stderr
+++ b/testsuite/tests/driver/T2464.stderr
@@ -1,3 +1,3 @@
-T2464.hs:3:16: warning: [-Wdeprecated-flags (in -Wdefault)]
+T2464.hs:3:17: warning: [-Wdeprecated-flags (in -Wdefault)]
-fffi is deprecated: use -XForeignFunctionInterface or pragma {-# LANGUAGE ForeignFunctionInterface #-} instead
diff --git a/testsuite/tests/driver/T2499.stderr b/testsuite/tests/driver/T2499.stderr
index 9a082d34b1..88d8dbe56a 100644
--- a/testsuite/tests/driver/T2499.stderr
+++ b/testsuite/tests/driver/T2499.stderr
@@ -1,4 +1,6 @@
-T2499.hs:1:12: unknown flag in {-# OPTIONS_GHC #-} pragma: -package
+T2499.hs:1:13: error:
+ unknown flag in {-# OPTIONS_GHC #-} pragma: -package
-T2499.hs:1:12: unknown flag in {-# OPTIONS_GHC #-} pragma: blargh
+T2499.hs:1:22: error:
+ unknown flag in {-# OPTIONS_GHC #-} pragma: blargh
diff --git a/testsuite/tests/driver/all.T b/testsuite/tests/driver/all.T
index 94ecb3006c..447a4d0800 100644
--- a/testsuite/tests/driver/all.T
+++ b/testsuite/tests/driver/all.T
@@ -234,6 +234,7 @@ test('T10970', normal, compile_and_run, [''])
test('T4931', normal, compile_and_run, [''])
test('T11182', normal, compile_and_run, [''])
test('T11381', normal, compile_fail, [''])
+test('StringListOptions', normal, compile, [''])
test('T11429a', normal, compile, ['-Wunrecognised-warning-flags -Wfoobar'])
test('T11429b', normal, compile, ['-Wno-unrecognised-warning-flags -Wfoobar'])
test('T11429c', normal, compile_fail, ['-Wunrecognised-warning-flags -Werror -Wfoobar'])
diff --git a/testsuite/tests/ghc-api/T18522-dbg-ppr.hs b/testsuite/tests/ghc-api/T18522-dbg-ppr.hs
index e0b6a57764..7c16e7f0d0 100644
--- a/testsuite/tests/ghc-api/T18522-dbg-ppr.hs
+++ b/testsuite/tests/ghc-api/T18522-dbg-ppr.hs
@@ -49,7 +49,7 @@ main = do
let (warnings, errors) = partitionMessages messages
case mres of
Nothing -> do
- printBagOfErrors logger dflags warnings
- printBagOfErrors logger dflags errors
+ printMessages logger dflags warnings
+ printMessages logger dflags errors
Just (t, _) -> do
putStrLn $ showSDoc dflags (debugPprType t)
diff --git a/testsuite/tests/parser/should_compile/T16619.stderr b/testsuite/tests/parser/should_compile/T16619.stderr
index 68a8bf5da8..aab7175abd 100644
--- a/testsuite/tests/parser/should_compile/T16619.stderr
+++ b/testsuite/tests/parser/should_compile/T16619.stderr
@@ -1,3 +1,3 @@
-T16619.hs:2:12: warning: [-Wdeprecated-flags (in -Wdefault)]
+T16619.hs:2:13: warning: [-Wdeprecated-flags (in -Wdefault)]
-Wmissing-space-after-bang is deprecated: bang patterns can no longer be written with a space
diff --git a/testsuite/tests/parser/should_fail/T16270.stderr b/testsuite/tests/parser/should_fail/T16270.stderr
index 6666ac7963..323d9c93e3 100644
--- a/testsuite/tests/parser/should_fail/T16270.stderr
+++ b/testsuite/tests/parser/should_fail/T16270.stderr
@@ -1,5 +1,5 @@
-T16270.hs:3:12: warning: [-Wdeprecated-flags (in -Wdefault)]
+T16270.hs:3:13: warning: [-Wdeprecated-flags (in -Wdefault)]
-Werror=missing-space-after-bang is deprecated: bang patterns can no longer be written with a space
T16270.hs:8:1: warning: [-Wtabs (in -Wdefault)]
diff --git a/testsuite/tests/parser/should_fail/readFail044.stderr b/testsuite/tests/parser/should_fail/readFail044.stderr
index 27becac67c..1976b56079 100644
--- a/testsuite/tests/parser/should_fail/readFail044.stderr
+++ b/testsuite/tests/parser/should_fail/readFail044.stderr
@@ -1,6 +1,6 @@
-readFail044.hs:3:16:
+readFail044.hs:3:17: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: -this-flag-does-not-exist
-readFail044.hs:3:16:
+readFail044.hs:3:43: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: -nor-does-this-one
diff --git a/testsuite/tests/parser/should_run/CountAstDeps.stdout b/testsuite/tests/parser/should_run/CountAstDeps.stdout
index a9479a9d8d..cb96d95d78 100644
--- a/testsuite/tests/parser/should_run/CountAstDeps.stdout
+++ b/testsuite/tests/parser/should_run/CountAstDeps.stdout
@@ -1,4 +1,4 @@
-Found 245 Language.Haskell.Syntax module dependencies
+Found 255 Language.Haskell.Syntax module dependencies
GHC.Builtin.Names
GHC.Builtin.PrimOps
GHC.Builtin.Types
@@ -83,6 +83,8 @@ GHC.Driver.CmdLine
GHC.Driver.Env
GHC.Driver.Env.Types
GHC.Driver.Errors
+GHC.Driver.Errors.Ppr
+GHC.Driver.Errors.Types
GHC.Driver.Flags
GHC.Driver.Hooks
GHC.Driver.Monad
@@ -103,12 +105,18 @@ GHC.Hs.Lit
GHC.Hs.Pat
GHC.Hs.Type
GHC.Hs.Utils
+GHC.HsToCore.Errors.Ppr
+GHC.HsToCore.Errors.Types
GHC.Iface.Ext.Fields
GHC.Iface.Recomp.Binary
GHC.Iface.Syntax
GHC.Iface.Type
GHC.Linker.Types
GHC.Parser.Annotation
+GHC.Parser.Errors
+GHC.Parser.Errors.Ppr
+GHC.Parser.Errors.Types
+GHC.Parser.Types
GHC.Platform
GHC.Platform.AArch64
GHC.Platform.ARM
@@ -138,6 +146,8 @@ GHC.StgToCmm.Types
GHC.SysTools.BaseDir
GHC.SysTools.Terminal
GHC.Tc.Errors.Hole.FitTypes
+GHC.Tc.Errors.Ppr
+GHC.Tc.Errors.Types
GHC.Tc.Types
GHC.Tc.Types.Constraint
GHC.Tc.Types.Evidence
diff --git a/testsuite/tests/parser/should_run/CountParserDeps.stdout b/testsuite/tests/parser/should_run/CountParserDeps.stdout
index 73a238fd09..82daac1a97 100644
--- a/testsuite/tests/parser/should_run/CountParserDeps.stdout
+++ b/testsuite/tests/parser/should_run/CountParserDeps.stdout
@@ -1,4 +1,4 @@
-Found 253 GHC.Parser module dependencies
+Found 261 GHC.Parser module dependencies
GHC.Builtin.Names
GHC.Builtin.PrimOps
GHC.Builtin.Types
@@ -84,6 +84,8 @@ GHC.Driver.CmdLine
GHC.Driver.Env
GHC.Driver.Env.Types
GHC.Driver.Errors
+GHC.Driver.Errors.Ppr
+GHC.Driver.Errors.Types
GHC.Driver.Flags
GHC.Driver.Hooks
GHC.Driver.Monad
@@ -104,6 +106,8 @@ GHC.Hs.Lit
GHC.Hs.Pat
GHC.Hs.Type
GHC.Hs.Utils
+GHC.HsToCore.Errors.Ppr
+GHC.HsToCore.Errors.Types
GHC.Iface.Ext.Fields
GHC.Iface.Recomp.Binary
GHC.Iface.Syntax
@@ -113,6 +117,8 @@ GHC.Parser
GHC.Parser.Annotation
GHC.Parser.CharClass
GHC.Parser.Errors
+GHC.Parser.Errors.Ppr
+GHC.Parser.Errors.Types
GHC.Parser.Lexer
GHC.Parser.PostProcess
GHC.Parser.PostProcess.Haddock
@@ -146,6 +152,8 @@ GHC.StgToCmm.Types
GHC.SysTools.BaseDir
GHC.SysTools.Terminal
GHC.Tc.Errors.Hole.FitTypes
+GHC.Tc.Errors.Ppr
+GHC.Tc.Errors.Types
GHC.Tc.Types
GHC.Tc.Types.Constraint
GHC.Tc.Types.Evidence
diff --git a/testsuite/tests/regalloc/regalloc_unit_tests.hs b/testsuite/tests/regalloc/regalloc_unit_tests.hs
index b956f2579a..4e84261264 100644
--- a/testsuite/tests/regalloc/regalloc_unit_tests.hs
+++ b/testsuite/tests/regalloc/regalloc_unit_tests.hs
@@ -124,7 +124,7 @@ compileCmmForRegAllocStats logger dflags' cmmFile ncgImplF us = do
errorMsgs = fmap mkParserErr errors
-- print parser errors or warnings
- mapM_ (printBagOfErrors logger dflags) [warningMsgs, errorMsgs]
+ mapM_ (printMessages logger dflags . mkMessages) [warningMsgs, errorMsgs]
let initTopSRT = emptySRT thisMod
cmmGroup <- fmap snd $ cmmPipeline hscEnv initTopSRT $ fst $ fromJust parsedCmm
diff --git a/testsuite/tests/safeHaskell/check/Check05.stderr b/testsuite/tests/safeHaskell/check/Check05.stderr
index 9db0875f44..c14f8c02b2 100644
--- a/testsuite/tests/safeHaskell/check/Check05.stderr
+++ b/testsuite/tests/safeHaskell/check/Check05.stderr
@@ -1,3 +1,3 @@
-Check05.hs:1:16:
+Check05.hs:1:17:
Warning: -fpackage-trust ignored; must be specified with a Safe Haskell flag
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags18.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags18.stderr
index 2766f41512..603cb636a7 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags18.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags18.stderr
@@ -1,3 +1,3 @@
-SafeFlags18.hs:1:16: error: [-Werror]
+SafeFlags18.hs:1:17: error: [-Werror]
-fpackage-trust ignored; must be specified with a Safe Haskell flag
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags19.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags19.stderr
index adbc757494..e47c40a558 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags19.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags19.stderr
@@ -1,3 +1,3 @@
-SafeFlags19.hs:1:16:
+SafeFlags19.hs:1:17:
unknown flag in {-# OPTIONS_GHC #-} pragma: -fno-package-trust
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags22.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags22.stderr
index 9d93e5e332..cc4d8b7ae1 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags22.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags22.stderr
@@ -1,5 +1,5 @@
-SafeFlags22.hs:2:16: warning: [-Wunsafe]
+SafeFlags22.hs:2:17: warning: [-Wunsafe]
‘SafeFlags22’ has been inferred as unsafe!
Reason:
SafeFlags22.hs:8:1: error:
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags23.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags23.stderr
index 6fef7a3e4c..3bb3d3b02a 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags23.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags23.stderr
@@ -1,5 +1,5 @@
-SafeFlags23.hs:2:16: error: [-Wunsafe, -Werror=unsafe]
+SafeFlags23.hs:2:17: error: [-Wunsafe, -Werror=unsafe]
‘SafeFlags22’ has been inferred as unsafe!
Reason:
SafeFlags23.hs:8:1: error:
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags25.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags25.stderr
index e26a813e83..57bcdaa192 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags25.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags25.stderr
@@ -1,3 +1,3 @@
-SafeFlags25.hs:2:16: warning: [-Wsafe]
+SafeFlags25.hs:2:17: warning: [-Wsafe]
‘SafeFlags25’ has been inferred as safe!
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags26.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags26.stderr
index dabbd92c00..f4b084a9a5 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags26.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags26.stderr
@@ -1,3 +1,3 @@
-SafeFlags26.hs:2:16: error: [-Wsafe, -Werror=safe]
+SafeFlags26.hs:2:17: error: [-Wsafe, -Werror=safe]
‘SafeFlags26’ has been inferred as safe!
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags28.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags28.stderr
index 46dcabb9fd..13fdaf0c1a 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags28.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags28.stderr
@@ -1,12 +1,12 @@
-SafeFlags28.hs:1:16:
+SafeFlags28.hs:1:17: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: -trust
-SafeFlags28.hs:1:16:
+SafeFlags28.hs:1:24: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: base,
-SafeFlags28.hs:1:16:
+SafeFlags28.hs:1:30: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: -trust
-SafeFlags28.hs:1:16:
+SafeFlags28.hs:1:37: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: bytestring
diff --git a/testsuite/tests/safeHaskell/flags/SafeFlags29.stderr b/testsuite/tests/safeHaskell/flags/SafeFlags29.stderr
index ee0d13b957..2c32e70254 100644
--- a/testsuite/tests/safeHaskell/flags/SafeFlags29.stderr
+++ b/testsuite/tests/safeHaskell/flags/SafeFlags29.stderr
@@ -1,12 +1,12 @@
-SafeFlags29.hs:2:16:
+SafeFlags29.hs:2:17: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: -trust
-SafeFlags29.hs:2:16:
+SafeFlags29.hs:2:24: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: base
-SafeFlags29.hs:2:16:
+SafeFlags29.hs:2:29: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: -trust
-SafeFlags29.hs:2:16:
+SafeFlags29.hs:2:36: error:
unknown flag in {-# OPTIONS_GHC #-} pragma: bytestring
diff --git a/testsuite/tests/safeHaskell/overlapping/SH_Overlap11.stderr b/testsuite/tests/safeHaskell/overlapping/SH_Overlap11.stderr
index 9eb029951f..0e990b4f97 100644
--- a/testsuite/tests/safeHaskell/overlapping/SH_Overlap11.stderr
+++ b/testsuite/tests/safeHaskell/overlapping/SH_Overlap11.stderr
@@ -2,7 +2,7 @@
[2 of 3] Compiling SH_Overlap11_A ( SH_Overlap11_A.hs, SH_Overlap11_A.o )
[3 of 3] Compiling SH_Overlap11 ( SH_Overlap11.hs, SH_Overlap11.o )
-SH_Overlap11.hs:2:16: warning: [-Wunsafe]
+SH_Overlap11.hs:2:17: warning: [-Wunsafe]
‘SH_Overlap11’ has been inferred as unsafe!
Reason:
SH_Overlap11.hs:18:8: warning:
diff --git a/testsuite/tests/safeHaskell/overlapping/SH_Overlap7.stderr b/testsuite/tests/safeHaskell/overlapping/SH_Overlap7.stderr
index 4a4fb3779c..dda1490ce0 100644
--- a/testsuite/tests/safeHaskell/overlapping/SH_Overlap7.stderr
+++ b/testsuite/tests/safeHaskell/overlapping/SH_Overlap7.stderr
@@ -2,7 +2,7 @@
[2 of 3] Compiling SH_Overlap7_A ( SH_Overlap7_A.hs, SH_Overlap7_A.o )
[3 of 3] Compiling SH_Overlap7 ( SH_Overlap7.hs, SH_Overlap7.o )
-SH_Overlap7.hs:2:16: error: [-Wunsafe, -Werror=unsafe]
+SH_Overlap7.hs:2:17: error: [-Wunsafe, -Werror=unsafe]
‘SH_Overlap7’ has been inferred as unsafe!
Reason:
SH_Overlap7.hs:15:8:
diff --git a/testsuite/tests/safeHaskell/safeInfered/SafeInfered05.stderr b/testsuite/tests/safeHaskell/safeInfered/SafeInfered05.stderr
index 2e9e92a696..664d3fa298 100644
--- a/testsuite/tests/safeHaskell/safeInfered/SafeInfered05.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/SafeInfered05.stderr
@@ -3,6 +3,6 @@ SafeInfered05.hs:3:14: warning: [-Wdeprecated-flags (in -Wdefault)]
-XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
[1 of 2] Compiling SafeInfered05_A ( SafeInfered05_A.hs, SafeInfered05_A.o )
-SafeInfered05_A.hs:3:16: warning: [-Wsafe]
+SafeInfered05_A.hs:3:17: warning: [-Wsafe]
‘SafeInfered05_A’ has been inferred as safe!
[2 of 2] Compiling SafeInfered05 ( SafeInfered05.hs, SafeInfered05.o )
diff --git a/testsuite/tests/safeHaskell/safeInfered/SafeWarn01.stderr b/testsuite/tests/safeHaskell/safeInfered/SafeWarn01.stderr
index 7efaba3490..13155f3043 100644
--- a/testsuite/tests/safeHaskell/safeInfered/SafeWarn01.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/SafeWarn01.stderr
@@ -1,3 +1,3 @@
-SafeWarn01.hs:3:16: warning: [-Wsafe]
+SafeWarn01.hs:3:17: warning: [-Wsafe]
‘SafeWarn01’ has been inferred as safe!
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered11.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered11.stderr
index 919eec4e6b..31a8488b74 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered11.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered11.stderr
@@ -1,6 +1,6 @@
[1 of 2] Compiling UnsafeInfered11_A ( UnsafeInfered11_A.hs, UnsafeInfered11_A.o )
-UnsafeInfered11_A.hs:2:16: warning: [-Wunsafe]
+UnsafeInfered11_A.hs:2:17: warning: [-Wunsafe]
‘UnsafeInfered11_A’ has been inferred as unsafe!
Reason:
UnsafeInfered11_A.hs:18:11: warning:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered12.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered12.stderr
index e3529474bd..22a07d9f96 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered12.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeInfered12.stderr
@@ -1,5 +1,5 @@
-UnsafeInfered12.hs:3:16: error: [-Wunsafe, -Werror=unsafe]
+UnsafeInfered12.hs:3:17: error: [-Wunsafe, -Werror=unsafe]
‘UnsafeInfered12’ has been inferred as unsafe!
Reason:
UnsafeInfered12.hs:2:14:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn01.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn01.stderr
index 5424c5f2a2..1bced3b94a 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn01.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn01.stderr
@@ -1,5 +1,5 @@
-UnsafeWarn01.hs:3:16: warning: [-Wunsafe]
+UnsafeWarn01.hs:3:17: warning: [-Wunsafe]
‘UnsafeWarn01’ has been inferred as unsafe!
Reason:
UnsafeWarn01.hs:8:1: error:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn02.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn02.stderr
index 881db587d5..0061ed3e1d 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn02.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn02.stderr
@@ -1,5 +1,5 @@
-UnsafeWarn02.hs:3:16: warning: [-Wunsafe]
+UnsafeWarn02.hs:3:17: warning: [-Wunsafe]
‘UnsafeWarn02’ has been inferred as unsafe!
Reason:
UnsafeWarn02.hs:5:14:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn03.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn03.stderr
index ba23b72ee1..c48a136f74 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn03.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn03.stderr
@@ -1,5 +1,5 @@
-UnsafeWarn03.hs:4:16: warning: [-Wunsafe]
+UnsafeWarn03.hs:4:17: warning: [-Wunsafe]
‘UnsafeWarn03’ has been inferred as unsafe!
Reason:
UnsafeWarn03.hs:9:1: error:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn04.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn04.stderr
index 90e6d5b6f1..94f0203f46 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn04.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn04.stderr
@@ -1,5 +1,5 @@
-UnsafeWarn04.hs:4:16: warning: [-Wunsafe]
+UnsafeWarn04.hs:4:17: warning: [-Wunsafe]
‘UnsafeWarn04’ has been inferred as unsafe!
Reason:
UnsafeWarn04.hs:9:1: error:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn05.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn05.stderr
index 2977504457..471443ea12 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn05.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn05.stderr
@@ -1,12 +1,12 @@
-UnsafeWarn05.hs:5:16: warning: [-Wunsafe]
+UnsafeWarn05.hs:5:17: warning: [-Wunsafe]
‘UnsafeWarn05’ has been inferred as unsafe!
Reason:
UnsafeWarn05.hs:11:1: error:
System.IO.Unsafe: Can't be safely imported!
The module itself isn't safe.
-UnsafeWarn05.hs:5:16: warning: [-Wunsafe]
+UnsafeWarn05.hs:5:17: warning: [-Wunsafe]
‘UnsafeWarn05’ has been inferred as unsafe!
Reason:
UnsafeWarn05.hs:16:11: warning:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn06.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn06.stderr
index 98b1360202..b494f02eec 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn06.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn06.stderr
@@ -1,5 +1,5 @@
-UnsafeWarn06.hs:4:16: warning: [-Wunsafe]
+UnsafeWarn06.hs:4:17: warning: [-Wunsafe]
‘UnsafeWarn06’ has been inferred as unsafe!
Reason:
UnsafeWarn06.hs:9:11: warning:
diff --git a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn07.stderr b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn07.stderr
index 91a4ec3547..0b3370cb59 100644
--- a/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn07.stderr
+++ b/testsuite/tests/safeHaskell/safeInfered/UnsafeWarn07.stderr
@@ -1,5 +1,5 @@
-UnsafeWarn07.hs:5:16: warning: [-Wunsafe]
+UnsafeWarn07.hs:5:17: warning: [-Wunsafe]
‘UnsafeWarn07’ has been inferred as unsafe!
Reason:
UnsafeWarn07.hs:10:11: warning:
diff --git a/testsuite/tests/th/T19709a.hs b/testsuite/tests/th/T19709a.hs
new file mode 100644
index 0000000000..8c0e5fd665
--- /dev/null
+++ b/testsuite/tests/th/T19709a.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE TemplateHaskell, ExplicitForAll, PolyKinds #-}
+
+module T19709a where
+
+import GHC.Exts
+
+$( let levid :: forall (r :: RuntimeRep) (a :: TYPE r). a -> a
+ levid x = x
+ in return [] )
diff --git a/testsuite/tests/th/T19709a.stderr b/testsuite/tests/th/T19709a.stderr
new file mode 100644
index 0000000000..4cb72aeed7
--- /dev/null
+++ b/testsuite/tests/th/T19709a.stderr
@@ -0,0 +1,6 @@
+
+T19709a.hs:8:14: error:
+ A levity-polymorphic type is not allowed here:
+ Type: a
+ Kind: TYPE r
+ In the type of binder ‘x’
diff --git a/testsuite/tests/th/T19709b.hs b/testsuite/tests/th/T19709b.hs
new file mode 100644
index 0000000000..afc9ed5769
--- /dev/null
+++ b/testsuite/tests/th/T19709b.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE TemplateHaskell, ExplicitForAll, PolyKinds, TypeApplications #-}
+
+module T19709b where
+
+import GHC.Exts
+import Language.Haskell.TH
+
+$( let levfun :: forall (r :: RuntimeRep) (a :: TYPE r). a -> ()
+ levfun = error "e1" -- NB: this, so far, is OK: no levity-polymorphic binder
+
+ in levfun (error @Any "e2") -- but this is very naughty: levity-polymorphic argument
+ `seq` return [] )
diff --git a/testsuite/tests/th/T19709b.stderr b/testsuite/tests/th/T19709b.stderr
new file mode 100644
index 0000000000..78405ebaea
--- /dev/null
+++ b/testsuite/tests/th/T19709b.stderr
@@ -0,0 +1,6 @@
+
+T19709b.hs:11:14: error:
+ A levity-polymorphic type is not allowed here:
+ Type: Any
+ Kind: TYPE Any
+ In the type of expression: (error @Any "e2")
diff --git a/testsuite/tests/th/T19709c.hs b/testsuite/tests/th/T19709c.hs
new file mode 100644
index 0000000000..588b269fc3
--- /dev/null
+++ b/testsuite/tests/th/T19709c.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -Wincomplete-patterns -Werror #-}
+
+module T19709c where
+
+import Language.Haskell.TH
+
+$( do runIO $ putStrLn "compiling the splice"
+ case tail "hello" of "hello" -> return [] )
diff --git a/testsuite/tests/th/T19709c.stderr b/testsuite/tests/th/T19709c.stderr
new file mode 100644
index 0000000000..3bedc08dc9
--- /dev/null
+++ b/testsuite/tests/th/T19709c.stderr
@@ -0,0 +1,10 @@
+
+T19709c.hs:9:7: error: [-Wincomplete-patterns (in -Wextra), -Werror=incomplete-patterns]
+ Pattern match(es) are non-exhaustive
+ In a case alternative:
+ Patterns of type ‘String’ not matched:
+ []
+ [p] where p is not one of {'h'}
+ (p:_:_) where p is not one of {'h'}
+ ['h']
+ ...
diff --git a/testsuite/tests/th/T19709d.hs b/testsuite/tests/th/T19709d.hs
new file mode 100644
index 0000000000..81985e80db
--- /dev/null
+++ b/testsuite/tests/th/T19709d.hs
@@ -0,0 +1,6 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -Wincomplete-patterns #-}
+
+module T19709d where
+
+$( case tail "hello" of "hello" -> return [] )
diff --git a/testsuite/tests/th/T19709d.stderr b/testsuite/tests/th/T19709d.stderr
new file mode 100644
index 0000000000..4adadd09ba
--- /dev/null
+++ b/testsuite/tests/th/T19709d.stderr
@@ -0,0 +1,16 @@
+
+T19709d.hs:6:4: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In a case alternative:
+ Patterns of type ‘String’ not matched:
+ []
+ [p] where p is not one of {'h'}
+ (p:_:_) where p is not one of {'h'}
+ ['h']
+ ...
+
+T19709d.hs:1:1: error:
+ Exception when trying to run compile-time code:
+ T19709d.hs:6:4-44: Non-exhaustive patterns in case
+
+ Code: (case tail "hello" of "hello" -> return [])
diff --git a/testsuite/tests/th/all.T b/testsuite/tests/th/all.T
index fb3bc7fb49..c34b92977a 100644
--- a/testsuite/tests/th/all.T
+++ b/testsuite/tests/th/all.T
@@ -514,6 +514,10 @@ test('TH_BytesShowEqOrd', normal, compile_and_run, [''])
test('T18102', normal, compile_fail, [''])
test('T18102b', extra_files(['T18102b_aux.hs']), compile_and_run, [''])
test('T18121', normal, compile, [''])
+test('T19709a', normal, compile_fail, [''])
+test('T19709b', normal, compile_fail, [''])
+test('T19709c', normal, compile_fail, [''])
+test('T19709d', normal, compile_fail, [''])
test('T18123', normal, compile, [''])
test('T18388', normal, compile, [''])
test('T18612', normal, compile, [''])
diff --git a/utils/check-exact/Parsers.hs b/utils/check-exact/Parsers.hs
index 03616f846a..93ddfd4f07 100644
--- a/utils/check-exact/Parsers.hs
+++ b/utils/check-exact/Parsers.hs
@@ -54,6 +54,7 @@ import qualified Control.Monad.IO.Class as GHC
import qualified GHC.Data.FastString as GHC
import qualified GHC.Data.StringBuffer as GHC
import qualified GHC.Driver.Config as GHC
+import qualified GHC.Driver.Errors.Types as GHC
import qualified GHC.Driver.Session as GHC
import qualified GHC.Parser as GHC
import qualified GHC.Parser.Header as GHC
@@ -61,7 +62,6 @@ import qualified GHC.Parser.Lexer as GHC
import qualified GHC.Parser.PostProcess as GHC
import qualified GHC.Parser.Errors.Ppr as GHC
import qualified GHC.Types.SrcLoc as GHC
-import qualified GHC.Utils.Error as GHC
import qualified GHC.LanguageExtensions as LangExt
@@ -79,8 +79,10 @@ parseWith :: GHC.DynFlags
-> ParseResult w
parseWith dflags fileName parser s =
case runParser parser dflags fileName s of
- GHC.PFailed pst -> Left (fmap GHC.mkParserErr $ GHC.getErrorMessages pst)
- GHC.POk _ pmod -> Right pmod
+ GHC.PFailed pst
+ -> Left (GHC.foldPsMessages GHC.mkParserErr (GHC.getErrorMessages pst))
+ GHC.POk _ pmod
+ -> Right pmod
parseWithECP :: (GHC.DisambECP w)
@@ -91,8 +93,10 @@ parseWithECP :: (GHC.DisambECP w)
-> ParseResult (GHC.LocatedA w)
parseWithECP dflags fileName parser s =
case runParser (parser >>= \p -> GHC.runPV $ GHC.unECP p) dflags fileName s of
- GHC.PFailed pst -> Left (fmap GHC.mkParserErr $ GHC.getErrorMessages pst)
- GHC.POk _ pmod -> Right pmod
+ GHC.PFailed pst
+ -> Left (GHC.foldPsMessages GHC.mkParserErr (GHC.getErrorMessages pst))
+ GHC.POk _ pmod
+ -> Right pmod
-- ---------------------------------------------------------------------
@@ -182,8 +186,10 @@ parseModuleFromStringInternal :: Parser GHC.ParsedSource
parseModuleFromStringInternal dflags fileName str =
let (str1, lp) = stripLinePragmas str
res = case runParser GHC.parseModule dflags fileName str1 of
- GHC.PFailed pst -> Left (fmap GHC.mkParserErr $ GHC.getErrorMessages pst)
- GHC.POk _ pmod -> Right (lp, dflags, pmod)
+ GHC.PFailed pst
+ -> Left (GHC.foldPsMessages GHC.mkParserErr (GHC.getErrorMessages pst))
+ GHC.POk _ pmod
+ -> Right (lp, dflags, pmod)
in postParseTransform res
parseModuleWithOptions :: FilePath -- ^ GHC libdir
@@ -253,9 +259,10 @@ parseModuleEpAnnsWithCppInternal cppOptions dflags file = do
return (contents1,lp,dflags)
return $
case parseFile dflags' file fileContents of
- GHC.PFailed pst -> Left (fmap GHC.mkParserErr $ GHC.getErrorMessages pst)
- GHC.POk _ pmod ->
- Right $ (injectedComments, dflags', pmod)
+ GHC.PFailed pst
+ -> Left (GHC.foldPsMessages GHC.mkParserErr (GHC.getErrorMessages pst))
+ GHC.POk _ pmod
+ -> Right $ (injectedComments, dflags', pmod)
-- | Internal function. Exposed if you want to muck with DynFlags
-- before parsing. Or after parsing.
diff --git a/utils/check-exact/Preprocess.hs b/utils/check-exact/Preprocess.hs
index 58cb6d028c..a2d3b53f49 100644
--- a/utils/check-exact/Preprocess.hs
+++ b/utils/check-exact/Preprocess.hs
@@ -16,26 +16,28 @@ module Preprocess
import qualified GHC as GHC hiding (parseModule)
import qualified Control.Monad.IO.Class as GHC
-import qualified GHC.Data.Bag as GHC
import qualified GHC.Data.FastString as GHC
import qualified GHC.Data.StringBuffer as GHC
import qualified GHC.Driver.Config as GHC
import qualified GHC.Driver.Env as GHC
+import qualified GHC.Driver.Errors.Types as GHC
import qualified GHC.Driver.Phases as GHC
import qualified GHC.Driver.Pipeline as GHC
import qualified GHC.Fingerprint.Type as GHC
import qualified GHC.Parser.Errors.Ppr as GHC
-import qualified GHC.Parser.Lexer as GHC
+import qualified GHC.Parser.Lexer as GHC hiding (getMessages)
import qualified GHC.Settings as GHC
+import qualified GHC.Types.Error as GHC (getMessages)
import qualified GHC.Types.SourceError as GHC
import qualified GHC.Types.SourceFile as GHC
import qualified GHC.Types.SrcLoc as GHC
import qualified GHC.Utils.Error as GHC
import qualified GHC.Utils.Fingerprint as GHC
+import qualified GHC.Utils.Outputable as GHC
import GHC.Types.SrcLoc (mkSrcSpan, mkSrcLoc)
import GHC.Data.FastString (mkFastString)
-import Data.List (isPrefixOf, intercalate)
+import Data.List (isPrefixOf)
import Data.Maybe
import Types
import Utils
@@ -221,8 +223,13 @@ getPreprocessedSrcDirectPrim cppOptions src_fn = do
txt <- GHC.liftIO $ readFileGhc hspp_fn
return (txt, buf, dflags')
-showErrorMessages :: GHC.ErrorMessages -> String
-showErrorMessages msgs = intercalate "\n" $ map show $ GHC.bagToList msgs
+showErrorMessages :: GHC.Messages GHC.DriverMessage -> String
+showErrorMessages msgs =
+ GHC.renderWithContext GHC.defaultSDocContext
+ $ GHC.vcat
+ $ GHC.pprMsgEnvelopeBagWithLoc
+ $ GHC.getMessages
+ $ msgs
injectCppOptions :: CppOptions -> GHC.DynFlags -> GHC.DynFlags
injectCppOptions CppOptions{..} dflags =
@@ -276,7 +283,8 @@ parseError pst = do
let
-- (warns,errs) = GHC.getMessages pst dflags
-- throw $ GHC.mkSrcErr (GHC.unitBag $ GHC.mkPlainErrMsg dflags sspan err)
- GHC.throwErrors (fmap GHC.mkParserErr (GHC.getErrorMessages pst))
+ GHC.throwErrors $
+ (GHC.foldPsMessages GHC.mkParserErr (GHC.getErrorMessages pst))
-- ---------------------------------------------------------------------