3
3
{-# LANGUAGE FlexibleContexts #-}
4
4
{-# LANGUAGE FlexibleInstances #-}
5
5
{-# LANGUAGE GADTs #-}
6
+ #if MIN_VERSION_base(4,21,0)
7
+ {-# LANGUAGE ImplicitParams #-}
8
+ #endif
6
9
{-# LANGUAGE InstanceSigs #-}
7
10
{-# LANGUAGE LambdaCase #-}
8
11
{-# LANGUAGE RankNTypes #-}
@@ -39,6 +42,7 @@ module Distribution.Simple.Utils
39
42
, dieNoWrap
40
43
, topHandler
41
44
, topHandlerWith
45
+ , isUserException
42
46
, warn
43
47
, warnError
44
48
, notice
@@ -301,6 +305,10 @@ import GitHash
301
305
)
302
306
#endif
303
307
308
+ #if MIN_VERSION_base(4,21,0)
309
+ import Control.Exception.Context
310
+ #endif
311
+
304
312
-- We only get our own version number when we're building with ourselves
305
313
cabalVersion :: Version
306
314
#if defined(BOOTSTRAPPED_CABAL)
@@ -505,9 +513,22 @@ ioeModifyErrorString = over ioeErrorString
505
513
ioeErrorString :: Lens' IOError String
506
514
ioeErrorString f ioe = ioeSetErrorString ioe <$> f (ioeGetErrorString ioe)
507
515
516
+ -- | Check that the type of the exception matches the given user error type.
517
+ isUserException :: forall user_err . Typeable user_err => Proxy user_err -> Exception. SomeException -> Bool
518
+ isUserException Proxy (SomeException se) =
519
+ case cast se :: Maybe user_err of
520
+ Just {} -> True
521
+ Nothing -> False
522
+
508
523
{-# NOINLINE topHandlerWith #-}
509
- topHandlerWith :: forall a . (Exception. SomeException -> IO a ) -> IO a -> IO a
510
- topHandlerWith cont prog = do
524
+ topHandlerWith
525
+ :: forall a
526
+ . (Exception. SomeException -> Bool )
527
+ -- ^ Identify when the error is an exception to display to users.
528
+ -> (Exception. SomeException -> IO a )
529
+ -> IO a
530
+ -> IO a
531
+ topHandlerWith is_user_exception cont prog = do
511
532
-- By default, stderr to a terminal device is NoBuffering. But this
512
533
-- is *really slow*
513
534
hSetBuffering stderr LineBuffering
@@ -535,7 +556,7 @@ topHandlerWith cont prog = do
535
556
cont se
536
557
537
558
message :: String -> Exception. SomeException -> String
538
- message pname (Exception. SomeException se) =
559
+ message pname e @ (Exception. SomeException se) =
539
560
case cast se :: Maybe Exception. IOException of
540
561
Just ioe
541
562
| ioeGetVerbatim ioe ->
@@ -550,15 +571,27 @@ topHandlerWith cont prog = do
550
571
_ -> " "
551
572
detail = ioeGetErrorString ioe
552
573
in wrapText $ addErrorPrefix $ pname ++ " : " ++ file ++ detail
553
- _ ->
554
- displaySomeException se ++ " \n "
574
+ -- Don't print a call stack for a "user exception"
575
+ _
576
+ | is_user_exception e -> displayException e
577
+ -- Other errors which have are not intended for user display, print with a callstack.
578
+ | otherwise -> displaySomeExceptionWithContext e ++ " \n "
555
579
556
580
-- | BC wrapper around 'Exception.displayException'.
557
- displaySomeException :: Exception. Exception e => e -> String
558
- displaySomeException se = Exception. displayException se
581
+ displaySomeExceptionWithContext :: SomeException -> String
582
+ #if MIN_VERSION_base(4,21,0)
583
+ displaySomeExceptionWithContext (SomeException e) =
584
+ case displayExceptionContext ? exceptionContext of
585
+ " " -> msg
586
+ dc -> msg ++ " \n\n " ++ dc
587
+ where
588
+ msg = displayException e
589
+ #else
590
+ displaySomeExceptionWithContext e = displayException e
591
+ #endif
559
592
560
- topHandler :: IO a -> IO a
561
- topHandler prog = topHandlerWith (const $ exitWith (ExitFailure 1 )) prog
593
+ topHandler :: ( Exception. SomeException -> Bool ) -> IO a -> IO a
594
+ topHandler is_user_exception prog = topHandlerWith is_user_exception (const $ exitWith (ExitFailure 1 )) prog
562
595
563
596
-- | Depending on 'isVerboseStderr', set the output handle to 'stderr' or 'stdout'.
564
597
verbosityHandle :: Verbosity -> Handle
0 commit comments