Bodigrim pushed to branch wip/warning-for-last-and-init at Glasgow Haskell Compiler / GHC

Commits:

17 changed files:

Changes:

  • compiler/GHC/Core/Utils.hs
    ... ... @@ -112,8 +112,7 @@ import GHC.Utils.Misc
    112 112
     
    
    113 113
     import Data.ByteString     ( ByteString )
    
    114 114
     import Data.Function       ( on )
    
    115
    -import Data.List           ( sort, sortBy, partition, zipWith4, mapAccumL )
    
    116
    -import qualified Data.List as Partial ( init, last )
    
    115
    +import Data.List           ( sort, sortBy, partition, zipWith4, mapAccumL, unsnoc )
    
    117 116
     import Data.Ord            ( comparing )
    
    118 117
     import Control.Monad       ( guard )
    
    119 118
     import qualified Data.Set as Set
    
    ... ... @@ -1871,10 +1870,10 @@ app_ok fun_ok primop_ok fun args
    1871 1870
     
    
    1872 1871
           PrimOpId op _
    
    1873 1872
             | primOpIsDiv op
    
    1874
    -        , Lit divisor <- Partial.last args
    
    1873
    +        , Just (initArgs, Lit divisor) <- unsnoc args
    
    1875 1874
                 -- there can be 2 args (most div primops) or 3 args
    
    1876 1875
                 -- (WordQuotRem2Op), hence the use of last/init
    
    1877
    -        -> not (isZeroLit divisor) && all (expr_ok fun_ok primop_ok) (Partial.init args)
    
    1876
    +        -> not (isZeroLit divisor) && all (expr_ok fun_ok primop_ok) initArgs
    
    1878 1877
                   -- Special case for dividing operations that fail
    
    1879 1878
                   -- In general they are NOT ok-for-speculation
    
    1880 1879
                   -- (which primop_ok will catch), but they ARE OK
    

  • compiler/GHC/Driver/Session/Units.hs
    ... ... @@ -183,7 +183,7 @@ checkUnitCycles dflags graph = processSCCs (HUG.hugSCCs graph)
    183 183
     
    
    184 184
         processSCCs [] = return ()
    
    185 185
         processSCCs (AcyclicSCC _: other_sccs) = processSCCs other_sccs
    
    186
    -    processSCCs (CyclicSCC uids: _) = throwGhcException $ CmdLineError $ showSDoc dflags (cycle_err uids)
    
    186
    +    processSCCs (NECyclicSCC uids: _) = throwGhcException $ CmdLineError $ showSDoc dflags (cycle_err uids)
    
    187 187
     
    
    188 188
     
    
    189 189
         cycle_err uids =
    
    ... ... @@ -195,8 +195,8 @@ checkUnitCycles dflags graph = processSCCs (HUG.hugSCCs graph)
    195 195
                         (map (\uid -> text "-" <+> ppr uid <+> text "depends on") start)
    
    196 196
                         ++ [text "-" <+> ppr final]
    
    197 197
           where
    
    198
    -        start = init uids
    
    199
    -        final = last uids
    
    198
    +        start = NE.init uids
    
    199
    +        final = NE.last uids
    
    200 200
     
    
    201 201
     -- | Check that we don't have multiple units with the same UnitId.
    
    202 202
     checkDuplicateUnits :: DynFlags -> [(FilePath, DynFlags)] -> Ghc ()
    

  • compiler/GHC/Prelude/Basic.hs
    ... ... @@ -2,8 +2,8 @@
    2 2
     {-# OPTIONS_HADDOCK not-home #-}
    
    3 3
     {-# OPTIONS_GHC -O2 #-} -- See Note [-O2 Prelude]
    
    4 4
     
    
    5
    --- See Note [Proxies for head and tail]
    
    6
    -{-# OPTIONS_GHC -Wno-unrecognised-warning-flags -Wno-x-partial #-}
    
    5
    +-- See Note [Proxies for partial list functions]
    
    6
    +{-# OPTIONS_GHC -Wno-x-partial #-}
    
    7 7
     
    
    8 8
     -- | Custom minimal GHC "Prelude"
    
    9 9
     --
    
    ... ... @@ -24,7 +24,7 @@ module GHC.Prelude.Basic
    24 24
       , bit
    
    25 25
       , shiftL, shiftR
    
    26 26
       , setBit, clearBit
    
    27
    -  , head, tail, unzip
    
    27
    +  , head, tail, init, last, unzip
    
    28 28
     
    
    29 29
       , strictGenericLength
    
    30 30
       ) where
    
    ... ... @@ -59,7 +59,7 @@ NoImplicitPrelude. There are two motivations for this:
    59 59
     -}
    
    60 60
     
    
    61 61
     import qualified Prelude
    
    62
    -import Prelude as X hiding ((<>), Applicative(..), Foldable(..), head, tail, unzip)
    
    62
    +import Prelude as X hiding ((<>), Applicative(..), Foldable(..), head, tail, init, last, unzip)
    
    63 63
     import Control.Applicative (Applicative(..))
    
    64 64
     import Data.Foldable as X (Foldable (elem, foldMap, foldl, foldl', foldr, length, null, product, sum))
    
    65 65
     import Data.Foldable1 as X hiding (head, last)
    
    ... ... @@ -118,24 +118,35 @@ setBit = \ x i -> x Bits..|. bit i
    118 118
     clearBit :: (Num a, Bits.Bits a) => a -> Int -> a
    
    119 119
     clearBit = \ x i -> x Bits..&. Bits.complement (bit i)
    
    120 120
     
    
    121
    -{- Note [Proxies for head and tail]
    
    121
    +{- Note [Proxies for partial list functions]
    
    122 122
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    123
    -Prelude.head and Prelude.tail have recently acquired {-# WARNING in "x-partial" #-},
    
    123
    +Prelude.head, Prelude.tail, Prelude.init and Prelude.last
    
    124
    +have recently acquired {-# WARNING in "x-partial" #-},
    
    124 125
     but the GHC codebase uses them fairly extensively and insists on building warning-free.
    
    125 126
     Thus, instead of adding {-# OPTIONS_GHC -Wno-x-partial #-} to every module which
    
    126 127
     employs them, we define warning-less proxies and export them from GHC.Prelude.
    
    127 128
     -}
    
    128 129
     
    
    129
    --- See Note [Proxies for head and tail]
    
    130
    +-- See Note [Proxies for partial list functions]
    
    130 131
     head :: HasCallStack => [a] -> a
    
    131 132
     head = Prelude.head
    
    132 133
     {-# INLINE head #-}
    
    133 134
     
    
    134
    --- See Note [Proxies for head and tail]
    
    135
    +-- See Note [Proxies for partial list functions]
    
    135 136
     tail :: HasCallStack => [a] -> [a]
    
    136 137
     tail = Prelude.tail
    
    137 138
     {-# INLINE tail #-}
    
    138 139
     
    
    140
    +-- See Note [Proxies for partial list functions]
    
    141
    +init :: HasCallStack => [a] -> [a]
    
    142
    +init = Prelude.init
    
    143
    +{-# INLINE init #-}
    
    144
    +
    
    145
    +-- See Note [Proxies for partial list functions]
    
    146
    +last :: HasCallStack => [a] -> a
    
    147
    +last = Prelude.last
    
    148
    +{-# INLINE last #-}
    
    149
    +
    
    139 150
     {- |
    
    140 151
     The 'genericLength' function defined in base can't be specialised due to the
    
    141 152
     NOINLINE pragma.
    

  • ghc/GHCi/UI.hs
    ... ... @@ -133,7 +133,7 @@ import Data.Char
    133 133
     import Data.Function
    
    134 134
     import qualified Data.Foldable as Foldable
    
    135 135
     import Data.IORef ( IORef, modifyIORef, newIORef, readIORef, writeIORef )
    
    136
    -import Data.List ( find, intercalate, intersperse,
    
    136
    +import Data.List ( find, intercalate, intersperse, unsnoc,
    
    137 137
                        isPrefixOf, isSuffixOf, nub, partition, sort, sortBy, (\\) )
    
    138 138
     import qualified Data.List.NonEmpty as NE
    
    139 139
     import qualified Data.Set as S
    
    ... ... @@ -2399,9 +2399,9 @@ setContextAfterLoad keep_ctxt (Just graph) = do
    2399 2399
             []    ->
    
    2400 2400
               let graph' = flattenSCCs $ filterToposortToModules $
    
    2401 2401
                     GHC.topSortModuleGraph True (GHC.mkModuleGraph loaded_graph) Nothing
    
    2402
    -          in case graph' of
    
    2403
    -              [] -> setContextKeepingPackageModules keep_ctxt []
    
    2404
    -              xs -> load_this (last xs)
    
    2402
    +          in case unsnoc graph' of
    
    2403
    +              Nothing -> setContextKeepingPackageModules keep_ctxt []
    
    2404
    +              Just (_, lst) -> load_this lst
    
    2405 2405
             (m:_) ->
    
    2406 2406
               load_this m
    
    2407 2407
      where
    

  • ghc/Main.hs
    ... ... @@ -88,7 +88,7 @@ import System.Exit
    88 88
     import Control.Monad
    
    89 89
     import Control.Monad.Trans.Class
    
    90 90
     import Control.Monad.Trans.Except (throwE, runExceptT)
    
    91
    -import Data.List ( isPrefixOf, partition, intercalate )
    
    91
    +import Data.List ( isPrefixOf, partition, intercalate, unsnoc )
    
    92 92
     import Prelude
    
    93 93
     import qualified Data.List.NonEmpty as NE
    
    94 94
     
    
    ... ... @@ -115,8 +115,7 @@ main = do
    115 115
         argv0 <- getArgs
    
    116 116
     
    
    117 117
         let (minusB_args, argv1) = partition ("-B" `isPrefixOf`) argv0
    
    118
    -        mbMinusB | null minusB_args = Nothing
    
    119
    -                 | otherwise = Just (drop 2 (last minusB_args))
    
    118
    +        mbMinusB = drop 2 . snd <$> unsnoc minusB_args
    
    120 119
     
    
    121 120
         let argv2 = map (mkGeneralLocated "on the commandline") argv1
    
    122 121
     
    

  • libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs
    ... ... @@ -13,7 +13,7 @@ import GHC.Boot.TH.PprLib
    13 13
     import GHC.Boot.TH.Syntax
    
    14 14
     import Data.Word ( Word8 )
    
    15 15
     import Data.Char ( toLower, chr )
    
    16
    -import Data.List ( intersperse )
    
    16
    +import Data.List ( intersperse, unsnoc )
    
    17 17
     import GHC.Show  ( showMultiLineString )
    
    18 18
     import GHC.Lexeme( isVarSymChar )
    
    19 19
     import Data.Ratio ( numerator, denominator )
    
    ... ... @@ -214,9 +214,10 @@ pprExp i (MDoE m ss_) = parensIf (i > noPrec) $
    214 214
         pprStms [s] = ppr s
    
    215 215
         pprStms ss  = braces (semiSep ss)
    
    216 216
     
    
    217
    -pprExp _ (CompE []) = text "<<Empty CompExp>>"
    
    218 217
     -- This will probably break with fixity declarations - would need a ';'
    
    219
    -pprExp _ (CompE ss) =
    
    218
    +pprExp _ (CompE ss) = case unsnoc ss of
    
    219
    +  Nothing -> text "<<Empty CompExp>>"
    
    220
    +  Just (ss', s) ->
    
    220 221
         if null ss'
    
    221 222
            -- If there are no statements in a list comprehension besides the last
    
    222 223
            -- one, we simply treat it like a normal list.
    
    ... ... @@ -225,8 +226,6 @@ pprExp _ (CompE ss) =
    225 226
             <+> bar
    
    226 227
             <+> commaSep ss'
    
    227 228
              <> text "]"
    
    228
    -  where s = last ss
    
    229
    -        ss' = init ss
    
    230 229
     pprExp _ (ArithSeqE d) = ppr d
    
    231 230
     pprExp _ (ListE es) = brackets (commaSep es)
    
    232 231
     pprExp i (SigE e t) = parensIf (i > noPrec) $ pprExp sigPrec e
    

  • libraries/ghc-internal/src/GHC/Internal/Float.hs
    ... ... @@ -13,6 +13,9 @@
    13 13
     {-# OPTIONS_HADDOCK not-home #-}
    
    14 14
     {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
    
    15 15
     
    
    16
    +-- For init in formatRealFloatAlt
    
    17
    +{-# OPTIONS_GHC -Wno-x-partial #-}
    
    18
    +
    
    16 19
     -----------------------------------------------------------------------------
    
    17 20
     -- |
    
    18 21
     -- Module      :  GHC.Internal.Float
    

  • libraries/ghc-internal/src/GHC/Internal/List.hs
    ... ... @@ -190,12 +190,18 @@ tail :: HasCallStack => [a] -> [a]
    190 190
     tail (_:xs)             =  xs
    
    191 191
     tail []                 =  errorEmptyList "tail"
    
    192 192
     
    
    193
    -{-# WARNING in "x-partial" tail "This is a partial function, it throws an error on empty lists. Replace it with 'drop' 1, or use pattern matching or 'GHC.Internal.Data.List.uncons' instead. Consider refactoring to use \"Data.List.NonEmpty\"." #-}
    
    193
    +{-# WARNING in "x-partial" tail "This is a partial function, it throws an error on empty lists. Replace it with 'drop' 1, or use pattern matching or 'Data.List.uncons' instead. Consider refactoring to use \"Data.List.NonEmpty\"." #-}
    
    194 194
     
    
    195 195
     -- | \(\mathcal{O}(n)\). Extract the last element of a list, which must be
    
    196 196
     -- finite and non-empty.
    
    197 197
     --
    
    198
    --- WARNING: This function is partial. Consider using 'unsnoc' instead.
    
    198
    +-- To disable the warning about partiality put
    
    199
    +-- @{-# OPTIONS_GHC -Wno-x-partial -Wno-unrecognised-warning-flags #-}@
    
    200
    +-- at the top of the file. To disable it throughout a package put the same
    
    201
    +-- options into @ghc-options@ section of Cabal file. To disable it in GHCi
    
    202
    +-- put @:set -Wno-x-partial -Wno-unrecognised-warning-flags@ into @~/.ghci@
    
    203
    +-- config file. See also the
    
    204
    +-- [migration guide](https://github.com/haskell/core-libraries-committee/blob/main/guides/warning-for-init-and-last.md).
    
    199 205
     --
    
    200 206
     -- ==== __Examples__
    
    201 207
     --
    
    ... ... @@ -218,10 +224,18 @@ last xs = foldl (\_ x -> x) lastError xs
    218 224
     lastError :: HasCallStack => a
    
    219 225
     lastError = errorEmptyList "last"
    
    220 226
     
    
    227
    +{-# WARNING in "x-partial" last "This is a partial function, it throws an error on empty lists. Use 'Data.List.unsnoc' instead. Consider refactoring to use \"Data.List.NonEmpty\"." #-}
    
    228
    +
    
    221 229
     -- | \(\mathcal{O}(n)\). Return all the elements of a list except the last one.
    
    222 230
     -- The list must be non-empty.
    
    223 231
     --
    
    224
    --- WARNING: This function is partial. Consider using 'unsnoc' instead.
    
    232
    +-- To disable the warning about partiality put
    
    233
    +-- @{-# OPTIONS_GHC -Wno-x-partial -Wno-unrecognised-warning-flags #-}@
    
    234
    +-- at the top of the file. To disable it throughout a package put the same
    
    235
    +-- options into @ghc-options@ section of Cabal file. To disable it in GHCi
    
    236
    +-- put @:set -Wno-x-partial -Wno-unrecognised-warning-flags@ into  @~/.ghci@
    
    237
    +-- config file. See also the
    
    238
    +-- [migration guide](https://github.com/haskell/core-libraries-committee/blob/main/guides/warning-for-init-and-last.md).
    
    225 239
     --
    
    226 240
     -- ==== __Examples__
    
    227 241
     --
    
    ... ... @@ -240,6 +254,8 @@ init (x:xs) = init' x xs
    240 254
       where init' _ []     = []
    
    241 255
             init' y (z:zs) = y : init' z zs
    
    242 256
     
    
    257
    +{-# WARNING in "x-partial" init "This is a partial function, it throws an error on empty lists. Use 'Data.List.unsnoc' instead. Consider refactoring to use \"Data.List.NonEmpty\"." #-}
    
    258
    +
    
    243 259
     -- | \(\mathcal{O}(1)\). Test whether a list is empty.
    
    244 260
     --
    
    245 261
     -- >>> null []
    

  • libraries/ghc-internal/src/GHC/Internal/System/IO.hs
    ... ... @@ -825,11 +825,12 @@ output_flags = std_flags
    825 825
     
    
    826 826
           where
    
    827 827
             -- XXX bits copied from System.FilePath, since that's not available here
    
    828
    -        combine a b
    
    829
    -                  | null b = a
    
    830
    -                  | null a = b
    
    831
    -                  | pathSeparator [last a] = a ++ b
    
    832
    -                  | otherwise = a ++ [pathSeparatorChar] ++ b
    
    828
    +        combine a [] = a
    
    829
    +        combine a b = case unsnoc a of
    
    830
    +            Nothing -> b
    
    831
    +            Just (_, lastA)
    
    832
    +                | pathSeparator [lastA] -> a ++ b
    
    833
    +                | otherwise -> a ++ [pathSeparatorChar] ++ b
    
    833 834
     
    
    834 835
     tempCounter :: IORef Int
    
    835 836
     tempCounter = unsafePerformIO $ newIORef 0
    

  • libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs
    ... ... @@ -54,6 +54,7 @@ import GHC.Internal.Base hiding (NonEmpty(..),Type, Module, sequence)
    54 54
     import GHC.Internal.Data.Data hiding (Fixity(..))
    
    55 55
     import GHC.Internal.Data.NonEmpty (NonEmpty(..))
    
    56 56
     import GHC.Internal.Data.Traversable
    
    57
    +import GHC.Internal.List (unsnoc)
    
    57 58
     import GHC.Internal.Word
    
    58 59
     import GHC.Internal.Generics (Generic)
    
    59 60
     import GHC.Internal.IORef
    
    ... ... @@ -73,7 +74,7 @@ import GHC.Internal.Control.Monad.Fix
    73 74
     import GHC.Internal.Control.Exception
    
    74 75
     import GHC.Internal.Num
    
    75 76
     import GHC.Internal.IO.Unsafe
    
    76
    -import GHC.Internal.List (dropWhile, break, replicate, reverse, last)
    
    77
    +import GHC.Internal.List (dropWhile, break, replicate, reverse)
    
    77 78
     import GHC.Internal.MVar
    
    78 79
     import GHC.Internal.IO.Exception
    
    79 80
     import GHC.Internal.Unicode
    
    ... ... @@ -82,6 +83,16 @@ import qualified GHC.Internal.Types as Kind (Type)
    82 83
     import GHC.Internal.ForeignSrcLang
    
    83 84
     import GHC.Internal.LanguageExtensions
    
    84 85
     
    
    86
    +#ifdef BOOTSTRAP_TH
    
    87
    +#if MIN_VERSION_base(4,19,0)
    
    88
    +import Data.List (unsnoc)
    
    89
    +#else
    
    90
    +import Data.Maybe (maybe)
    
    91
    +unsnoc :: [a] -> Maybe ([a], a)
    
    92
    +unsnoc = foldr (\x -> Just . maybe ([], x) (\(~(a, b)) -> (x : a, b))) Nothing
    
    93
    +#endif
    
    94
    +#endif
    
    95
    +
    
    85 96
     -----------------------------------------------------
    
    86 97
     --
    
    87 98
     --              The Quasi class
    
    ... ... @@ -1296,7 +1307,7 @@ mkName str
    1296 1307
         --   (i.e. non-empty, starts with capital, all alpha)
    
    1297 1308
         is_rev_mod_name rev_mod_str
    
    1298 1309
           | (compt, rest) <- break (== '.') rev_mod_str
    
    1299
    -      , not (null compt), isUpper (last compt), all is_mod_char compt
    
    1310
    +      , Just (_, lastCompt) <- unsnoc compt, isUpper lastCompt, all is_mod_char compt
    
    1300 1311
           = case rest of
    
    1301 1312
               []             -> True
    
    1302 1313
               (_dot : rest') -> is_rev_mod_name rest'
    

  • libraries/template-haskell/vendored-filepath/System/FilePath/Posix.hs
    ... ... @@ -104,7 +104,7 @@ module System.FilePath.Posix
    104 104
     
    
    105 105
     import Data.Char(toLower, toUpper, isAsciiLower, isAsciiUpper)
    
    106 106
     import Data.Maybe(isJust)
    
    107
    -import Data.List(stripPrefix, isSuffixOf)
    
    107
    +import Data.List(stripPrefix, isSuffixOf, uncons, unsnoc)
    
    108 108
     
    
    109 109
     import System.Environment(getEnv)
    
    110 110
     
    
    ... ... @@ -203,14 +203,20 @@ isExtSeparator = (== extSeparator)
    203 203
     splitSearchPath :: String -> [FilePath]
    
    204 204
     splitSearchPath = f
    
    205 205
         where
    
    206
    -    f xs = case break isSearchPathSeparator xs of
    
    207
    -           (pre, []    ) -> g pre
    
    208
    -           (pre, _:post) -> g pre ++ f post
    
    209
    -
    
    210
    -    g "" = ["." | isPosix]
    
    211
    -    g ('\"':x@(_:_)) | isWindows && last x == '\"' = [init x]
    
    212
    -    g x = [x]
    
    213
    -
    
    206
    +    f xs = let (pre, post) = break isSearchPathSeparator xs
    
    207
    +           in case uncons post of
    
    208
    +             Nothing     -> g pre
    
    209
    +             Just (_, t) -> g pre ++ f t
    
    210
    +
    
    211
    +    g x = case uncons x of
    
    212
    +      Nothing -> ["." | isPosix]
    
    213
    +      Just (h, t)
    
    214
    +        | h == '"'
    
    215
    +        , Just{} <- uncons t -- >= 2
    
    216
    +        , isWindows
    
    217
    +        , Just (i, l) <- unsnoc t
    
    218
    +        , l == '"' -> [i]
    
    219
    +        | otherwise -> [x]
    
    214 220
     
    
    215 221
     -- | Get a list of 'FilePath's in the $PATH variable.
    
    216 222
     getSearchPath :: IO [FilePath]
    
    ... ... @@ -233,12 +239,17 @@ getSearchPath = fmap splitSearchPath (getEnv "PATH")
    233 239
     -- > splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
    
    234 240
     -- > splitExtension "file/path.txt/" == ("file/path.txt/","")
    
    235 241
     splitExtension :: FilePath -> (String, String)
    
    236
    -splitExtension x = case nameDot of
    
    237
    -                       "" -> (x,"")
    
    238
    -                       _ -> (dir ++ init nameDot, extSeparator : ext)
    
    239
    -    where
    
    240
    -        (dir,file) = splitFileName_ x
    
    241
    -        (nameDot,ext) = breakEnd isExtSeparator file
    
    242
    +splitExtension x = case unsnoc nameDot of
    
    243
    +  -- Imagine x = "no-dots", then nameDot = ""
    
    244
    +  Nothing -> (x, mempty)
    
    245
    +  Just (initNameDot, _)
    
    246
    +    -- Imagine x = "\\shared.with.dots\no-dots"
    
    247
    +    | isWindows && null (dropDrive nameDot) -> (x, mempty)
    
    248
    +    -- Imagine x = "dir.with.dots/no-dots"
    
    249
    +    | any isPathSeparator ext -> (x, mempty)
    
    250
    +    | otherwise -> (initNameDot, extSeparator : ext)
    
    251
    +  where
    
    252
    +    (nameDot, ext) = breakEnd isExtSeparator x
    
    242 253
     
    
    243 254
     -- | Get the extension of a file, returns @\"\"@ for no extension, @.ext@ otherwise.
    
    244 255
     --
    
    ... ... @@ -594,9 +605,13 @@ replaceBaseName pth nam = combineAlways a (nam <.> ext)
    594 605
     -- > hasTrailingPathSeparator "test" == False
    
    595 606
     -- > hasTrailingPathSeparator "test/" == True
    
    596 607
     hasTrailingPathSeparator :: FilePath -> Bool
    
    597
    -hasTrailingPathSeparator "" = False
    
    598
    -hasTrailingPathSeparator x = isPathSeparator (last x)
    
    608
    +hasTrailingPathSeparator = isJust . getTrailingPathSeparator
    
    599 609
     
    
    610
    +getTrailingPathSeparator :: FilePath -> Maybe Char
    
    611
    +getTrailingPathSeparator x = case unsnoc x of
    
    612
    +  Just (_, lastX)
    
    613
    +    | isPathSeparator lastX -> Just lastX
    
    614
    +  _ -> Nothing
    
    600 615
     
    
    601 616
     hasLeadingPathSeparator :: FilePath -> Bool
    
    602 617
     hasLeadingPathSeparator "" = False
    
    ... ... @@ -619,12 +634,12 @@ addTrailingPathSeparator x = if hasTrailingPathSeparator x then x else x ++ [pat
    619 634
     -- > Windows:  dropTrailingPathSeparator "\\" == "\\"
    
    620 635
     -- > Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
    
    621 636
     dropTrailingPathSeparator :: FilePath -> FilePath
    
    622
    -dropTrailingPathSeparator x =
    
    623
    -    if hasTrailingPathSeparator x && not (isDrive x)
    
    624
    -    then let x' = dropWhileEnd isPathSeparator x
    
    625
    -         in if null x' then [last x] else x'
    
    626
    -    else x
    
    627
    -
    
    637
    +dropTrailingPathSeparator x = case getTrailingPathSeparator x of
    
    638
    +  Just lastX
    
    639
    +    | not (isDrive x)
    
    640
    +    -> let x' = dropWhileEnd isPathSeparator x
    
    641
    +        in if null x' then [lastX] else x'
    
    642
    +  _ -> x
    
    628 643
     
    
    629 644
     -- | Get the directory name, move up one level.
    
    630 645
     --
    
    ... ... @@ -863,28 +878,37 @@ makeRelative root path
    863 878
     -- > Posix:   normalise "bob/fred/." == "bob/fred/"
    
    864 879
     -- > Posix:   normalise "//home" == "/home"
    
    865 880
     normalise :: FilePath -> FilePath
    
    866
    -normalise path = result ++ [pathSeparator | addPathSeparator]
    
    867
    -    where
    
    868
    -        (drv,pth) = splitDrive path
    
    869
    -        result = joinDrive' (normaliseDrive drv) (f pth)
    
    881
    +normalise filepath =
    
    882
    +  result <>
    
    883
    +  (if addPathSeparator
    
    884
    +       then [pathSeparator]
    
    885
    +       else mempty)
    
    886
    +  where
    
    887
    +    (drv,pth) = splitDrive filepath
    
    888
    +
    
    889
    +    result = joinDrive' (normaliseDrive drv) (f pth)
    
    870 890
     
    
    871
    -        joinDrive' "" "" = "."
    
    872
    -        joinDrive' d p = joinDrive d p
    
    891
    +    joinDrive' d p
    
    892
    +      = if null d && null p
    
    893
    +           then "."
    
    894
    +           else joinDrive d p
    
    873 895
     
    
    874
    -        addPathSeparator = isDirPath pth
    
    875
    -            && not (hasTrailingPathSeparator result)
    
    876
    -            && not (isRelativeDrive drv)
    
    896
    +    addPathSeparator = isDirPath pth
    
    897
    +      && not (hasTrailingPathSeparator result)
    
    898
    +      && not (isRelativeDrive drv)
    
    877 899
     
    
    878
    -        isDirPath xs = hasTrailingPathSeparator xs
    
    879
    -            || not (null xs) && last xs == '.' && hasTrailingPathSeparator (init xs)
    
    900
    +    isDirPath xs = hasTrailingPathSeparator xs || case unsnoc xs of
    
    901
    +      Nothing -> False
    
    902
    +      Just (initXs, lastXs) -> lastXs == '.' && hasTrailingPathSeparator initXs
    
    880 903
     
    
    881
    -        f = joinPath . dropDots . propSep . splitDirectories
    
    904
    +    f = joinPath . dropDots . propSep . splitDirectories
    
    882 905
     
    
    883
    -        propSep (x:xs) | all isPathSeparator x = [pathSeparator] : xs
    
    884
    -                       | otherwise = x : xs
    
    885
    -        propSep [] = []
    
    906
    +    propSep (x:xs)
    
    907
    +      | all isPathSeparator x = [pathSeparator] : xs
    
    908
    +      | otherwise             = x : xs
    
    909
    +    propSep [] = []
    
    886 910
     
    
    887
    -        dropDots = filter ("." /=)
    
    911
    +    dropDots = filter ("." /=)
    
    888 912
     
    
    889 913
     normaliseDrive :: FilePath -> FilePath
    
    890 914
     normaliseDrive "" = ""
    

  • libraries/template-haskell/vendored-filepath/System/FilePath/Windows.hs
    ... ... @@ -104,7 +104,7 @@ module System.FilePath.Windows
    104 104
     
    
    105 105
     import Data.Char(toLower, toUpper, isAsciiLower, isAsciiUpper)
    
    106 106
     import Data.Maybe(isJust)
    
    107
    -import Data.List(stripPrefix, isSuffixOf)
    
    107
    +import Data.List(stripPrefix, isSuffixOf, uncons, unsnoc)
    
    108 108
     
    
    109 109
     import System.Environment(getEnv)
    
    110 110
     
    
    ... ... @@ -203,14 +203,20 @@ isExtSeparator = (== extSeparator)
    203 203
     splitSearchPath :: String -> [FilePath]
    
    204 204
     splitSearchPath = f
    
    205 205
         where
    
    206
    -    f xs = case break isSearchPathSeparator xs of
    
    207
    -           (pre, []    ) -> g pre
    
    208
    -           (pre, _:post) -> g pre ++ f post
    
    209
    -
    
    210
    -    g "" = ["." | isPosix]
    
    211
    -    g ('\"':x@(_:_)) | isWindows && last x == '\"' = [init x]
    
    212
    -    g x = [x]
    
    213
    -
    
    206
    +    f xs = let (pre, post) = break isSearchPathSeparator xs
    
    207
    +           in case uncons post of
    
    208
    +             Nothing     -> g pre
    
    209
    +             Just (_, t) -> g pre ++ f t
    
    210
    +
    
    211
    +    g x = case uncons x of
    
    212
    +      Nothing -> ["." | isPosix]
    
    213
    +      Just (h, t)
    
    214
    +        | h == '"'
    
    215
    +        , Just{} <- uncons t -- >= 2
    
    216
    +        , isWindows
    
    217
    +        , Just (i, l) <- unsnoc t
    
    218
    +        , l == '"' -> [i]
    
    219
    +        | otherwise -> [x]
    
    214 220
     
    
    215 221
     -- | Get a list of 'FilePath's in the $PATH variable.
    
    216 222
     getSearchPath :: IO [FilePath]
    
    ... ... @@ -233,12 +239,17 @@ getSearchPath = fmap splitSearchPath (getEnv "PATH")
    233 239
     -- > splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
    
    234 240
     -- > splitExtension "file/path.txt/" == ("file/path.txt/","")
    
    235 241
     splitExtension :: FilePath -> (String, String)
    
    236
    -splitExtension x = case nameDot of
    
    237
    -                       "" -> (x,"")
    
    238
    -                       _ -> (dir ++ init nameDot, extSeparator : ext)
    
    239
    -    where
    
    240
    -        (dir,file) = splitFileName_ x
    
    241
    -        (nameDot,ext) = breakEnd isExtSeparator file
    
    242
    +splitExtension x = case unsnoc nameDot of
    
    243
    +  -- Imagine x = "no-dots", then nameDot = ""
    
    244
    +  Nothing -> (x, mempty)
    
    245
    +  Just (initNameDot, _)
    
    246
    +    -- Imagine x = "\\shared.with.dots\no-dots"
    
    247
    +    | isWindows && null (dropDrive nameDot) -> (x, mempty)
    
    248
    +    -- Imagine x = "dir.with.dots/no-dots"
    
    249
    +    | any isPathSeparator ext -> (x, mempty)
    
    250
    +    | otherwise -> (initNameDot, extSeparator : ext)
    
    251
    +  where
    
    252
    +    (nameDot, ext) = breakEnd isExtSeparator x
    
    242 253
     
    
    243 254
     -- | Get the extension of a file, returns @\"\"@ for no extension, @.ext@ otherwise.
    
    244 255
     --
    
    ... ... @@ -594,9 +605,13 @@ replaceBaseName pth nam = combineAlways a (nam <.> ext)
    594 605
     -- > hasTrailingPathSeparator "test" == False
    
    595 606
     -- > hasTrailingPathSeparator "test/" == True
    
    596 607
     hasTrailingPathSeparator :: FilePath -> Bool
    
    597
    -hasTrailingPathSeparator "" = False
    
    598
    -hasTrailingPathSeparator x = isPathSeparator (last x)
    
    608
    +hasTrailingPathSeparator = isJust . getTrailingPathSeparator
    
    599 609
     
    
    610
    +getTrailingPathSeparator :: FilePath -> Maybe Char
    
    611
    +getTrailingPathSeparator x = case unsnoc x of
    
    612
    +  Just (_, lastX)
    
    613
    +    | isPathSeparator lastX -> Just lastX
    
    614
    +  _ -> Nothing
    
    600 615
     
    
    601 616
     hasLeadingPathSeparator :: FilePath -> Bool
    
    602 617
     hasLeadingPathSeparator "" = False
    
    ... ... @@ -619,12 +634,12 @@ addTrailingPathSeparator x = if hasTrailingPathSeparator x then x else x ++ [pat
    619 634
     -- > Windows:  dropTrailingPathSeparator "\\" == "\\"
    
    620 635
     -- > Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
    
    621 636
     dropTrailingPathSeparator :: FilePath -> FilePath
    
    622
    -dropTrailingPathSeparator x =
    
    623
    -    if hasTrailingPathSeparator x && not (isDrive x)
    
    624
    -    then let x' = dropWhileEnd isPathSeparator x
    
    625
    -         in if null x' then [last x] else x'
    
    626
    -    else x
    
    627
    -
    
    637
    +dropTrailingPathSeparator x = case getTrailingPathSeparator x of
    
    638
    +  Just lastX
    
    639
    +    | not (isDrive x)
    
    640
    +    -> let x' = dropWhileEnd isPathSeparator x
    
    641
    +        in if null x' then [lastX] else x'
    
    642
    +  _ -> x
    
    628 643
     
    
    629 644
     -- | Get the directory name, move up one level.
    
    630 645
     --
    
    ... ... @@ -863,28 +878,37 @@ makeRelative root path
    863 878
     -- > Posix:   normalise "bob/fred/." == "bob/fred/"
    
    864 879
     -- > Posix:   normalise "//home" == "/home"
    
    865 880
     normalise :: FilePath -> FilePath
    
    866
    -normalise path = result ++ [pathSeparator | addPathSeparator]
    
    867
    -    where
    
    868
    -        (drv,pth) = splitDrive path
    
    869
    -        result = joinDrive' (normaliseDrive drv) (f pth)
    
    881
    +normalise filepath =
    
    882
    +  result <>
    
    883
    +  (if addPathSeparator
    
    884
    +       then [pathSeparator]
    
    885
    +       else mempty)
    
    886
    +  where
    
    887
    +    (drv,pth) = splitDrive filepath
    
    888
    +
    
    889
    +    result = joinDrive' (normaliseDrive drv) (f pth)
    
    870 890
     
    
    871
    -        joinDrive' "" "" = "."
    
    872
    -        joinDrive' d p = joinDrive d p
    
    891
    +    joinDrive' d p
    
    892
    +      = if null d && null p
    
    893
    +           then "."
    
    894
    +           else joinDrive d p
    
    873 895
     
    
    874
    -        addPathSeparator = isDirPath pth
    
    875
    -            && not (hasTrailingPathSeparator result)
    
    876
    -            && not (isRelativeDrive drv)
    
    896
    +    addPathSeparator = isDirPath pth
    
    897
    +      && not (hasTrailingPathSeparator result)
    
    898
    +      && not (isRelativeDrive drv)
    
    877 899
     
    
    878
    -        isDirPath xs = hasTrailingPathSeparator xs
    
    879
    -            || not (null xs) && last xs == '.' && hasTrailingPathSeparator (init xs)
    
    900
    +    isDirPath xs = hasTrailingPathSeparator xs || case unsnoc xs of
    
    901
    +      Nothing -> False
    
    902
    +      Just (initXs, lastXs) -> lastXs == '.' && hasTrailingPathSeparator initXs
    
    880 903
     
    
    881
    -        f = joinPath . dropDots . propSep . splitDirectories
    
    904
    +    f = joinPath . dropDots . propSep . splitDirectories
    
    882 905
     
    
    883
    -        propSep (x:xs) | all isPathSeparator x = [pathSeparator] : xs
    
    884
    -                       | otherwise = x : xs
    
    885
    -        propSep [] = []
    
    906
    +    propSep (x:xs)
    
    907
    +      | all isPathSeparator x = [pathSeparator] : xs
    
    908
    +      | otherwise             = x : xs
    
    909
    +    propSep [] = []
    
    886 910
     
    
    887
    -        dropDots = filter ("." /=)
    
    911
    +    dropDots = filter ("." /=)
    
    888 912
     
    
    889 913
     normaliseDrive :: FilePath -> FilePath
    
    890 914
     normaliseDrive "" = ""
    

  • testsuite/tests/driver/j-space/jspace.hs
    ... ... @@ -7,7 +7,7 @@ import System.Environment
    7 7
     import GHC.Driver.Env.Types
    
    8 8
     import GHC.Profiling
    
    9 9
     import System.Mem
    
    10
    -import Data.List (isPrefixOf)
    
    10
    +import Data.List (isPrefixOf, unsnoc)
    
    11 11
     import Control.Monad
    
    12 12
     import System.Exit
    
    13 13
     import GHC.Platform
    
    ... ... @@ -41,7 +41,9 @@ initGhcM xs = do
    41 41
           requestHeapCensus
    
    42 42
           performGC
    
    43 43
           [ys] <- filter (isPrefixOf (ghcUnitId <> ":GHC.Unit.Module.ModDetails.ModDetails")) . lines <$> readFile "jspace.hp"
    
    44
    -      let (n :: Int) = read (last (words ys))
    
    44
    +      let (n :: Int) = case unsnoc (words ys) of
    
    45
    +            Nothing -> error "input is unexpectedly empty"
    
    46
    +            Just (_, lst) -> read lst
    
    45 47
           -- The output should be 50 * 8 * word_size (i.e. 3600, or 1600 on 32-bit architectures):
    
    46 48
           -- the test contains DEPTH + WIDTH + 2 = 50 modules J, H_0, .., H_DEPTH, W_1, .., W_WIDTH,
    
    47 49
           -- and each ModDetails contains 1 (info table) + 8 word-sized fields.
    

  • testsuite/tests/rts/KeepCafsBase.hs
    1
    +{-# OPTIONS_GHC -Wno-x-partial #-}
    
    2
    +
    
    1 3
     module KeepCafsBase (x) where
    
    2 4
     
    
    3 5
     x :: Int
    

  • utils/check-exact/Utils.hs
    ... ... @@ -37,7 +37,7 @@ import GHC.Base (NonEmpty(..))
    37 37
     import GHC.Parser.Lexer (allocateComments)
    
    38 38
     
    
    39 39
     import Data.Data hiding ( Fixity )
    
    40
    -import Data.List (sortBy, partition)
    
    40
    +import Data.List (sortBy, partition, unsnoc)
    
    41 41
     import qualified Data.Map.Strict as Map
    
    42 42
     
    
    43 43
     import Debug.Trace
    
    ... ... @@ -734,8 +734,9 @@ ghead info [] = error $ "ghead "++info++" []"
    734 734
     ghead _info (h:_) = h
    
    735 735
     
    
    736 736
     glast :: String -> [a] -> a
    
    737
    -glast  info []    = error $ "glast " ++ info ++ " []"
    
    738
    -glast _info h     = last h
    
    737
    +glast info xs = case unsnoc xs of
    
    738
    +  Nothing       -> error $ "glast " ++ info ++ " []"
    
    739
    +  Just (_, lst) -> lst
    
    739 740
     
    
    740 741
     gtail :: String -> [a] -> [a]
    
    741 742
     gtail  info []    = error $ "gtail " ++ info ++ " []"
    

  • utils/haddock/haddock-api/src/Haddock/Backends/Xhtml.hs
    ... ... @@ -755,7 +755,7 @@ ppHtmlIndex
    755 755
             divAlphabet
    
    756 756
               << unordList
    
    757 757
                 ( map (\str -> anchor ! [href (subIndexHtmlFile str)] << str) $
    
    758
    -                [ [c] | c <- initialChars, any ((== c) . toUpper . head . fst) index
    
    758
    +                [ [c] | c <- initialChars, any (maybe False ((== c) . toUpper . fst) . List.uncons . fst) index
    
    759 759
                     ]
    
    760 760
                       ++ [merged_name]
    
    761 761
                 )
    
    ... ... @@ -772,7 +772,7 @@ ppHtmlIndex
    772 772
               writeUtf8File (joinPath [odir, subIndexHtmlFile [c]]) (renderToString debug html)
    
    773 773
             where
    
    774 774
               html = indexPage True (Just c) index_part
    
    775
    -          index_part = [(n, stuff) | (n, stuff) <- this_ix, toUpper (head n) == c]
    
    775
    +          index_part = [(n, stuff) | (n@(headN : _), stuff) <- this_ix, toUpper headN == c]
    
    776 776
     
    
    777 777
           index :: [(String, Map GHC.Name [(Module, Bool)])]
    
    778 778
           index = sortBy cmp (Map.toAscList full_index)
    

  • utils/haddock/haddock-library/src/Documentation/Haddock/Parser.hs
    ... ... @@ -30,7 +30,7 @@ import Control.Arrow (first)
    30 30
     import Control.Monad
    
    31 31
     import Data.Char (chr, isAlpha, isSpace, isUpper)
    
    32 32
     import Data.Functor (($>))
    
    33
    -import Data.List (elemIndex, intercalate, intersperse, unfoldr)
    
    33
    +import Data.List (elemIndex, intercalate, intersperse, unfoldr, unsnoc)
    
    34 34
     import Data.Maybe (fromMaybe, mapMaybe)
    
    35 35
     import Data.Monoid
    
    36 36
     import qualified Data.Set as Set
    
    ... ... @@ -870,10 +870,10 @@ codeblock =
    870 870
       DocCodeBlock . parseParagraph . dropSpaces
    
    871 871
         <$> ("@" *> skipHorizontalSpace *> "\n" *> block' <* "@")
    
    872 872
       where
    
    873
    -    dropSpaces xs =
    
    874
    -      case splitByNl xs of
    
    875
    -        [] -> xs
    
    876
    -        ys -> case T.uncons (last ys) of
    
    873
    +    dropSpaces xs = let ys = splitByNl xs in
    
    874
    +      case unsnoc ys of
    
    875
    +        Nothing -> xs
    
    876
    +        Just (_, lastYs) -> case T.uncons lastYs of
    
    877 877
               Just (' ', _) -> case mapM dropSpace ys of
    
    878 878
                 Nothing -> xs
    
    879 879
                 Just zs -> T.intercalate "\n" zs