Commit 0593e938 authored by Zejun Wu's avatar Zejun Wu Committed by Ben Gamari

Add -fdefer-diagnostics to defer and group diagnostic messages in make-mode

When loading many modules in parallel there can a lot of warnings and
errors get mixed up with regular output. When the compilation fails,
the relevant error message can be thousands of lines backward and is
hard to find. When the compilation successes, warning message is likely
to be ignored as it is not seen. We can address this by deferring the
warning and error message after the compilation. We also put errors
after warnings so it is more visible.

This idea was originally proposed by Bartosz Nitka in
parent 98ff3010
......@@ -582,6 +582,7 @@ data GeneralFlag
-- output style opts
| Opt_ErrorSpans -- Include full span info in error messages,
-- instead of just the start position.
| Opt_DeferDiagnostics
| Opt_DiagnosticsShowCaret -- Show snippets of offending code
| Opt_PprCaseAsLet
| Opt_PprShowTicks
......@@ -4101,6 +4102,7 @@ fFlagsDeps = [
flagSpec "stg-cse" Opt_StgCSE,
flagSpec "stg-lift-lams" Opt_StgLiftLams,
flagSpec "cpr-anal" Opt_CprAnal,
flagSpec "defer-diagnostics" Opt_DeferDiagnostics,
flagSpec "defer-type-errors" Opt_DeferTypeErrors,
flagSpec "defer-typed-holes" Opt_DeferTypedHoles,
flagSpec "defer-out-of-scope-variables" Opt_DeferOutOfScopeVariables,
......@@ -395,8 +395,8 @@ load' how_much mHscMessage mod_graph = do
| otherwise = upsweep
setSession hsc_env{ hsc_HPT = emptyHomePackageTable }
(upsweep_ok, modsUpswept)
<- upsweep_fn mHscMessage pruned_hpt stable_mods cleanup mg
(upsweep_ok, modsUpswept) <- withDeferredDiagnostics $
upsweep_fn mHscMessage pruned_hpt stable_mods cleanup mg
-- Make modsDone be the summaries for each home module now
-- available; this should equal the domain of hpt3.
......@@ -2457,6 +2457,41 @@ preprocessFile hsc_env src_fn mb_phase (Just (buf, _time))
-- Error messages
-- Defer and group warning, error and fatal messages so they will not get lost
-- in the regular output.
withDeferredDiagnostics :: GhcMonad m => m a -> m a
withDeferredDiagnostics f = do
dflags <- getDynFlags
if not $ gopt Opt_DeferDiagnostics dflags
then f
else do
warnings <- liftIO $ newIORef []
errors <- liftIO $ newIORef []
fatals <- liftIO $ newIORef []
let deferDiagnostics _dflags !reason !severity !srcSpan !style !msg = do
let action = putLogMsg dflags reason severity srcSpan style msg
case severity of
SevWarning -> atomicModifyIORef' warnings $ \i -> (action: i, ())
SevError -> atomicModifyIORef' errors $ \i -> (action: i, ())
SevFatal -> atomicModifyIORef' fatals $ \i -> (action: i, ())
_ -> action
printDeferredDiagnostics = liftIO $
forM_ [warnings, errors, fatals] $ \ref -> do
-- This IORef can leak when the dflags leaks, so let us always
-- reset the content.
actions <- atomicModifyIORef' ref $ \i -> ([], i)
sequence_ $ reverse actions
setLogAction action = modifySession $ \hsc_env ->
hsc_env{ hsc_dflags = (hsc_dflags hsc_env){ log_action = action } }
(setLogAction deferDiagnostics)
(\_ -> setLogAction (log_action dflags) >> printDeferredDiagnostics)
(\_ -> f)
noModError :: DynFlags -> SrcSpan -> ModuleName -> FindResult -> ErrMsg
-- ToDo: we don't have a proper line number for this error
noModError dflags loc wanted_mod err
......@@ -931,6 +931,18 @@ messages and in GHCi:
in a’
or by using the flag -fno-warn-unused-do-bind
.. ghc-flag:: -fdefer-diagnostics
:shortdesc: Defer and group diagnostic messages by severity
:type: dynamic
:category: verbosity
Causes GHC to group diagnostic messages by severity and output them after
other messages when building a multi-module Haskell program. This flag can
make diagnostic messages more visible when used in conjunction with
:ghc-flag:`--make` and :ghc-flag:`-j[⟨n⟩]`. Otherwise, it can be hard to
find the relevant errors or likely to ignore the warnings when they are
mixed with many other messages.
.. ghc-flag:: -fdiagnostics-color=⟨always|auto|never⟩
:shortdesc: Use colors in error messages
:type: dynamic
{-# OPTIONS_GHC -Wincomplete-patterns -Wunused-matches #-}
module A where
incompletePattern :: Int -> Int
incompletePattern 0 = 0
unusedMatches :: Int -> Int
unusedMatches x = 0
{-# OPTIONS_GHC -Wunused-imports #-}
module B
( module A
) where
import A
import Data.List
module C where
import B
foo :: ()
foo = variableNotInScope
include $(TOP)/mk/
include $(TOP)/mk/
# testcase for warning and error messages from :load
test('prog018', [combined_output, extra_files(['A.hs', 'B.hs', 'C.hs'])],
ghci_script, ['prog018.script'])
:set -fdefer-diagnostics
:set -v1
:load C.hs
[1 of 3] Compiling A ( A.hs, interpreted )
[2 of 3] Compiling B ( B.hs, interpreted )
[3 of 3] Compiling C ( C.hs, interpreted )
A.hs:5:1: warning: [-Wincomplete-patterns (in -Wextra)]
Pattern match(es) are non-exhaustive
In an equation for ‘incompletePattern’:
Patterns not matched: p where p is not one of {0}
A.hs:8:15: warning: [-Wunused-matches (in -Wextra)]
Defined but not used: ‘x’
B.hs:7:1: warning: [-Wunused-imports (in -Wextra)]
The import of ‘Data.List’ is redundant
except perhaps to import instances from ‘Data.List’
To import instances alone, use: import Data.List()
C.hs:6:7: error: Variable not in scope: variableNotInScope :: ()
Failed, two modules loaded.
[3 of 3] Compiling C ( C.hs, interpreted )
C.hs:6:7: error: Variable not in scope: variableNotInScope :: ()
Failed, two modules loaded.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment