on 78615ce help sign in
.travis.yml+33−15
since splitsnapshots
unifiedsplit
@@ -25,46 +25,64 @@ before_cache:
25matrix:
25matrix:
26 include:
26 include:
27
27
28+ # Do the special build steps and handle the newest and oldest
29+ # GHCs first, since these are the most likely to fail.
30+
31+ # HLint
28 - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
32 - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
29 compiler: ": #GHC 8.4.4"
33 compiler: ": #GHC 8.4.4"
30 addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
34 addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
31
35
36+ # attoparsec-iso8601
37+ - env: BUILD=attoparsec-iso8601 CABALVER=3.0 GHCVER=8.10.1
38+ compiler: ": #GHC 8.10.1"
39+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.10.1], sources: [hvr-ghc]}}
40+
41+ # Latest GHC
42+ - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.10.1
43+ compiler: ": #GHC 8.10.1"
44+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.10.1], sources: [hvr-ghc]}}
45+
46+ # Oldest GHC
47+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
48+ compiler: ": #GHC 7.8.4"
49+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
50+
51+ # Benchmarks
32 - env: BUILD=stack CABALVER=2.4 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
52 - env: BUILD=stack CABALVER=2.4 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
33 compiler: ": #GHC 8.6.5"
53 compiler: ": #GHC 8.6.5"
34 addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
54 addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
35
55
56+ # C unescape implementation
36 - env: BUILD=stack CABALVER=2.2 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
57 - env: BUILD=stack CABALVER=2.2 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
37 compiler: ": #GHC 8.6.5"
58 compiler: ": #GHC 8.6.5"
38 addons: {apt: {packages: [cabal-install-2.2,ghc-8.6.5], sources: [hvr-ghc]}}
59 addons: {apt: {packages: [cabal-install-2.2,ghc-8.6.5], sources: [hvr-ghc]}}
39
60
40− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.1
41− compiler: ": #GHC 8.8.1"
42− addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}}
43
61
62+ - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.3
63+ compiler: ": #GHC 8.8.3"
64+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.3], sources: [hvr-ghc]}}
65+
44− - env: BUILD=cabal CABALVER=2.4 GHCVER=8.6.5
66+ - env: BUILD=cabal2 CABALVER=2.4 GHCVER=8.6.5
45 compiler: ": #GHC 8.6.5"
67 compiler: ": #GHC 8.6.5"
46− addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
68+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}}
47
69
48− - env: BUILD=cabal CABALVER=2.2 GHCVER=8.4.4
70+ - env: BUILD=cabal2 CABALVER=2.2 GHCVER=8.4.4
49 compiler: ": #GHC 8.4.4"
71 compiler: ": #GHC 8.4.4"
50− addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
72+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.4.4], sources: [hvr-ghc]}}
51
73
52− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.2.2
74+ - env: BUILD=cabal2 CABALVER=2.0 GHCVER=8.2.2
53 compiler: ": #GHC 8.2.2"
75 compiler: ": #GHC 8.2.2"
54− addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
76+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.2.2], sources: [hvr-ghc]}}
55
77
56− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.0.2
78+ - env: BUILD=cabal2 CABALVER=2.0 GHCVER=8.0.2
57 compiler: ": #GHC 8.0.2"
79 compiler: ": #GHC 8.0.2"
58− addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
80+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.0.2], sources: [hvr-ghc]}}
59
81
60 - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
82 - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
61 compiler: ": #GHC 7.10.3"
83 compiler: ": #GHC 7.10.3"
62 addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
84 addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
63
85
64− - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
65− compiler: ": #GHC 7.8.4"
66− addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
67−
68before_install:
86before_install:
69 - unset CC
87 - unset CC
70 - export PATH=$HOME/.local/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
88 - export PATH=$HOME/.local/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
Data/Aeson.hs+3−3
@@ -149,7 +149,7 @@ import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
149import Data.Aeson.Encoding (encodingToLazyByteString)
149import Data.Aeson.Encoding (encodingToLazyByteString)
150import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
150import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
151import Data.Aeson.Types
151import Data.Aeson.Types
152−import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
152+import Data.Aeson.Types.Internal (formatError)
153import qualified Data.ByteString as B
153import qualified Data.ByteString as B
154import qualified Data.ByteString.Lazy as L
154import qualified Data.ByteString.Lazy as L
155
155
@@ -257,7 +257,7 @@ eitherDecodeStrict =
257-- | Like 'decodeFileStrict' but returns an error message when decoding fails.
257-- | Like 'decodeFileStrict' but returns an error message when decoding fails.
258eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
258eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
259eitherDecodeFileStrict =
259eitherDecodeFileStrict =
260− fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON) . B.readFile
260+ fmap eitherDecodeStrict . B.readFile
261{-# INLINE eitherDecodeFileStrict #-}
261{-# INLINE eitherDecodeFileStrict #-}
262
262
263-- | Like 'decode'' but returns an error message when decoding fails.
263-- | Like 'decode'' but returns an error message when decoding fails.
@@ -274,7 +274,7 @@ eitherDecodeStrict' =
274-- | Like 'decodeFileStrict'' but returns an error message when decoding fails.
274-- | Like 'decodeFileStrict'' but returns an error message when decoding fails.
275eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
275eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
276eitherDecodeFileStrict' =
276eitherDecodeFileStrict' =
277− fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON) . B.readFile
277+ fmap eitherDecodeStrict' . B.readFile
278{-# INLINE eitherDecodeFileStrict' #-}
278{-# INLINE eitherDecodeFileStrict' #-}
279
279
280-- $use
280-- $use
Data/Aeson/Encoding/Builder.hs−1
@@ -44,7 +44,6 @@ import Data.ByteString.Builder as B
44import Data.ByteString.Builder.Prim as BP
44import Data.ByteString.Builder.Prim as BP
45import Data.ByteString.Builder.Scientific (scientificBuilder)
45import Data.ByteString.Builder.Scientific (scientificBuilder)
46import Data.Char (chr, ord)
46import Data.Char (chr, ord)
47−import Data.Semigroup ((<>))
48import Data.Scientific (Scientific, base10Exponent, coefficient)
47import Data.Scientific (Scientific, base10Exponent, coefficient)
49import Data.Text.Encoding (encodeUtf8BuilderEscaped)
48import Data.Text.Encoding (encodeUtf8BuilderEscaped)
50import Data.Time (UTCTime(..))
49import Data.Time (UTCTime(..))
Data/Aeson/Encoding/Internal.hs−1
@@ -63,7 +63,6 @@ import Data.Aeson.Types.Internal (Value)
63import Data.ByteString.Builder (Builder, char7, toLazyByteString)
63import Data.ByteString.Builder (Builder, char7, toLazyByteString)
64import Data.Int
64import Data.Int
65import Data.Scientific (Scientific)
65import Data.Scientific (Scientific)
66−import Data.Semigroup (Semigroup ((<>)))
67import Data.Text (Text)
66import Data.Text (Text)
68import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
67import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
69import Data.Typeable (Typeable)
68import Data.Typeable (Typeable)
Data/Aeson/Parser/Internal.hs+45−46
@@ -2,12 +2,9 @@
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
5−#if MIN_VERSION_ghc_prim(0,3,1)
6−{-# LANGUAGE MagicHash #-}
7−#endif
8#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
5#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
9-- Work around a compiler bug
6-- Work around a compiler bug
10−{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
7+{-# OPTIONS_GHC -fsimpl-tick-factor=300 #-}
11#endif
8#endif
12-- |
9-- |
13-- Module: Data.Aeson.Parser.Internal
10-- Module: Data.Aeson.Parser.Internal
@@ -58,8 +55,10 @@ import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Val
58import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
55import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
59import Data.Function (fix)
56import Data.Function (fix)
60import Data.Functor.Compat (($>))
57import Data.Functor.Compat (($>))
58+import Data.Bits (testBit)
61import Data.Scientific (Scientific)
59import Data.Scientific (Scientific)
62import Data.Text (Text)
60import Data.Text (Text)
61+import qualified Data.Text.Encoding as TE
63import Data.Vector (Vector)
62import Data.Vector (Vector)
64import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
63import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
65import qualified Data.Attoparsec.ByteString as A
64import qualified Data.Attoparsec.ByteString as A
@@ -67,16 +66,13 @@ import qualified Data.Attoparsec.Lazy as L
67import qualified Data.ByteString as B
66import qualified Data.ByteString as B
68import qualified Data.ByteString.Unsafe as B
67import qualified Data.ByteString.Unsafe as B
69import qualified Data.ByteString.Lazy as L
68import qualified Data.ByteString.Lazy as L
69+import qualified Data.ByteString.Lazy as BSL
70+import qualified Data.ByteString.Lazy.Char8 as C
71+import qualified Data.ByteString.Builder as B
70import qualified Data.HashMap.Strict as H
72import qualified Data.HashMap.Strict as H
71import qualified Data.Scientific as Sci
73import qualified Data.Scientific as Sci
72import Data.Aeson.Parser.Unescape (unescapeText)
74import Data.Aeson.Parser.Unescape (unescapeText)
73
75
74−#if MIN_VERSION_ghc_prim(0,3,1)
75−import GHC.Base (Int#, (==#), isTrue#, word2Int#, orI#, andI#)
76−import GHC.Word (Word8(W8#))
77−import qualified Data.Text.Encoding as TE
78−#endif
79−
80#define BACKSLASH 92
76#define BACKSLASH 92
81#define CLOSE_CURLY 125
77#define CLOSE_CURLY 125
82#define CLOSE_SQUARE 93
78#define CLOSE_SQUARE 93
@@ -323,37 +319,20 @@ jstring = A.word8 DOUBLE_QUOTE *> jstring_
323-- | Parse a string without a leading quote.
319-- | Parse a string without a leading quote.
324jstring_ :: Parser Text
320jstring_ :: Parser Text
325{-# INLINE jstring_ #-}
321{-# INLINE jstring_ #-}
326−jstring_ = {-# SCC "jstring_" #-} do
322+jstring_ = do
327−#if MIN_VERSION_ghc_prim(0,3,1)
323+ s <- A.takeWhile (\w -> w /= DOUBLE_QUOTE && w /= BACKSLASH && not (testBit w 7))
328− (s, S _ escaped) <- A.runScanner startState go <* A.anyWord8
324+ let txt = TE.decodeUtf8 s
329− -- We escape only if there are
325+ w <- A.peekWord8
330− -- non-ascii (over 7bit) characters or backslash present.
331− --
332− -- Note: if/when text will have fast ascii -> text conversion
333− -- (e.g. uses utf8 encoding) we can have further speedup.
334− if isTrue# escaped
335− then case unescapeText s of
326+ case w of
336− Right r -> return r
327+ Nothing -> fail "string without end"
337− Left err -> fail $ show err
328+ Just DOUBLE_QUOTE -> A.anyWord8 $> txt
338− else return (TE.decodeUtf8 s)
329+ _ -> jstringSlow s
339− where
330+
340− startState = S 0# 0#
331+jstringSlow :: B.ByteString -> Parser Text
341− go (S skip escaped) (W8# c)
332+{-# INLINE jstringSlow #-}
342− | isTrue# skip = Just (S 0# escaped')
333+jstringSlow s' = {-# SCC "jstringSlow" #-} do
343− | isTrue# (w ==# 34#) = Nothing -- double quote
344− | otherwise = Just (S skip' escaped')
345− where
346− w = word2Int# c
347− skip' = w ==# 92# -- backslash
348− escaped' = escaped
349− `orI#` (w `andI#` 0x80# ==# 0x80#) -- c >= 0x80
350− `orI#` skip'
351− `orI#` (w `andI#` 0x1f# ==# w) -- c < 0x20
352−
353−data S = S Int# Int#
354−#else
355 s <- A.scan startState go <* A.anyWord8
334 s <- A.scan startState go <* A.anyWord8
356− case unescapeText s of
335+ case unescapeText (B.append s' s) of
357 Right r -> return r
336 Right r -> return r
358 Left err -> fail $ show err
337 Left err -> fail $ show err
359 where
338 where
@@ -364,7 +343,6 @@ data S = S Int# Int#
364 | otherwise = let a' = c == backslash
343 | otherwise = let a' = c == backslash
365 in Just a'
344 in Just a'
366 where backslash = BACKSLASH
345 where backslash = BACKSLASH
367−#endif
368
346
369decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
347decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
370decodeWith p to s =
348decodeWith p to s =
@@ -390,14 +368,35 @@ eitherDecodeWith p to s =
390 L.Done _ v -> case to v of
368 L.Done _ v -> case to v of
391 ISuccess a -> Right a
369 ISuccess a -> Right a
392 IError path msg -> Left (path, msg)
370 IError path msg -> Left (path, msg)
393− L.Fail _ ctx msg -> Left ([], buildMsg ctx msg)
371+ L.Fail notparsed ctx msg -> Left ([], buildMsg notparsed ctx msg)
394 where
372 where
395− buildMsg :: [String] -> String -> String
373+ buildMsg :: L.ByteString -> [String] -> String -> String
396− buildMsg [] msg = msg
374+ buildMsg notYetParsed [] msg = msg ++ formatErrorLine notYetParsed
397− buildMsg (expectation:_) msg =
375+ buildMsg notYetParsed (expectation:_) msg =
398− msg ++ ". Expecting " ++ expectation
376+ msg ++ ". Expecting " ++ expectation ++ formatErrorLine notYetParsed
399{-# INLINE eitherDecodeWith #-}
377{-# INLINE eitherDecodeWith #-}
400
378
379+-- | Grab the first 100 bytes from the non parsed portion and
380+-- format to get nicer error messages
381+formatErrorLine :: L.ByteString -> String
382+formatErrorLine bs =
383+ C.unpack .
384+ -- if formatting results in empty ByteString just return that
385+ -- otherwise construct the error message with the bytestring builder
386+ (\bs' ->
387+ if BSL.null bs'
388+ then BSL.empty
389+ else
390+ B.toLazyByteString $
391+ B.stringUtf8 " at '" <> B.lazyByteString bs' <> B.stringUtf8 "'"
392+ ) .
393+ -- if newline is present cut at that position
394+ BSL.takeWhile (10 /=) .
395+ -- remove spaces, CR's, tabs, backslashes and quotes characters
396+ BSL.filter (`notElem` [9, 13, 32, 34, 47, 92]) .
397+ -- take 100 bytes
398+ BSL.take 100 $ bs
399+
401eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
400eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
402 -> Either (JSONPath, String) a
401 -> Either (JSONPath, String) a
403eitherDecodeStrictWith p to s =
402eitherDecodeStrictWith p to s =
Data/Aeson/TH.hs+41−14
@@ -116,7 +116,10 @@ module Data.Aeson.TH
116 , mkLiftParseJSON2
116 , mkLiftParseJSON2
117 ) where
117 ) where
118
118
119−import Prelude.Compat
119+import Prelude.Compat hiding (fail)
120+
121+-- We don't have MonadFail Q, so we should use `fail` from real `Prelude`
122+import Prelude (fail)
120
123
121import Control.Applicative ((<|>))
124import Control.Applicative ((<|>))
122import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
125import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
@@ -1304,9 +1307,8 @@ dispatchFunByType jc jf conName tvMap list ty = do
1304 tyVarNames :: [Name]
1307 tyVarNames :: [Name]
1305 tyVarNames = M.keys tvMap
1308 tyVarNames = M.keys tvMap
1306
1309
1307− itf <- isTyFamily tyCon
1310+ itf <- isInTypeFamilyApp tyVarNames tyCon tyArgs
1308− if any (`mentionsName` tyVarNames) lhsArgs
1311+ if any (`mentionsName` tyVarNames) lhsArgs || itf
1309− || itf && any (`mentionsName` tyVarNames) tyArgs
1310 then outOfPlaceTyVarError jc conName
1312 then outOfPlaceTyVarError jc conName
1311 else if any (`mentionsName` tyVarNames) rhsArgs
1313 else if any (`mentionsName` tyVarNames) rhsArgs
1312 then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
1314 then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
@@ -1657,19 +1659,44 @@ isTyVar (VarT _) = True
1657isTyVar (SigT t _) = isTyVar t
1659isTyVar (SigT t _) = isTyVar t
1658isTyVar _ = False
1660isTyVar _ = False
1659
1661
1660−-- | Is the given type a type family constructor (and not a data family constructor)?
1662+-- | Detect if a Name in a list of provided Names occurs as an argument to some
1661−isTyFamily :: Type -> Q Bool
1663+-- type family. This makes an effort to exclude /oversaturated/ arguments to
1662−isTyFamily (ConT n) = do
1664+-- type families. For instance, if one declared the following type family:
1665+--
1666+-- @
1667+-- type family F a :: Type -> Type
1668+-- @
1669+--
1670+-- Then in the type @F a b@, we would consider @a@ to be an argument to @F@,
1671+-- but not @b@.
1672+isInTypeFamilyApp :: [Name] -> Type -> [Type] -> Q Bool
1673+isInTypeFamilyApp names tyFun tyArgs =
1674+ case tyFun of
1675+ ConT tcName -> go tcName
1676+ _ -> return False
1677+ where
1678+ go :: Name -> Q Bool
1679+ go tcName = do
1663− info <- reify n
1680+ info <- reify tcName
1664− return $ case info of
1681+ case info of
1665#if MIN_VERSION_template_haskell(2,11,0)
1682#if MIN_VERSION_template_haskell(2,11,0)
1666− FamilyI OpenTypeFamilyD{} _ -> True
1683+ FamilyI (OpenTypeFamilyD (TypeFamilyHead _ bndrs _ _)) _
1684+ -> withinFirstArgs bndrs
1685+ FamilyI (ClosedTypeFamilyD (TypeFamilyHead _ bndrs _ _) _) _
1686+ -> withinFirstArgs bndrs
1667#else
1687#else
1668− FamilyI (FamilyD TypeFam _ _ _) _ -> True
1688+ FamilyI (FamilyD TypeFam _ bndrs _) _
1689+ -> withinFirstArgs bndrs
1690+ FamilyI (ClosedTypeFamilyD _ bndrs _ _) _
1691+ -> withinFirstArgs bndrs
1669#endif
1692#endif
1670− FamilyI ClosedTypeFamilyD{} _ -> True
1693+ _ -> return False
1671− _ -> False
1694+ where
1672−isTyFamily _ = return False
1695+ withinFirstArgs :: [a] -> Q Bool
1696+ withinFirstArgs bndrs =
1697+ let firstArgs = take (length bndrs) tyArgs
1698+ argFVs = freeVariables firstArgs
1699+ in return $ any (`elem` argFVs) names
1673
1700
1674-- | Peel off a kind signature from a Type (if it has one).
1701-- | Peel off a kind signature from a Type (if it has one).
1675unSigT :: Type -> Type
1702unSigT :: Type -> Type
Data/Aeson/Text.hs−1
@@ -26,7 +26,6 @@ import Prelude.Compat
26
26
27import Data.Aeson.Types (Value(..), ToJSON(..))
27import Data.Aeson.Types (Value(..), ToJSON(..))
28import Data.Aeson.Encoding (encodingToLazyByteString)
28import Data.Aeson.Encoding (encodingToLazyByteString)
29−import Data.Semigroup ((<>))
30import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
29import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
31import Data.Text.Lazy.Builder
30import Data.Text.Lazy.Builder
32import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
31import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
Data/Aeson/Types.hs+1
@@ -35,6 +35,7 @@ module Data.Aeson.Types
35 , parse
35 , parse
36 , parseEither
36 , parseEither
37 , parseMaybe
37 , parseMaybe
38+ , parseFail
38 , ToJSON(..)
39 , ToJSON(..)
39 , KeyValue(..)
40 , KeyValue(..)
40 , modifyFailure
41 , modifyFailure
Data/Aeson/Types/FromJSON.hs−1
@@ -96,7 +96,6 @@ import Data.Hashable (Hashable(..))
96import Data.Int (Int16, Int32, Int64, Int8)
96import Data.Int (Int16, Int32, Int64, Int8)
97import Data.List.NonEmpty (NonEmpty(..))
97import Data.List.NonEmpty (NonEmpty(..))
98import Data.Maybe (fromMaybe)
98import Data.Maybe (fromMaybe)
99−import Data.Semigroup ((<>))
100import Data.Proxy (Proxy(..))
99import Data.Proxy (Proxy(..))
101import Data.Ratio ((%), Ratio)
100import Data.Ratio ((%), Ratio)
102import Data.Scientific (Scientific, base10Exponent)
101import Data.Scientific (Scientific, base10Exponent)
Data/Aeson/Types/Internal.hs+33−14
@@ -44,6 +44,7 @@ module Data.Aeson.Types.Internal
44 , parse
44 , parse
45 , parseEither
45 , parseEither
46 , parseMaybe
46 , parseMaybe
47+ , parseFail
47 , modifyFailure
48 , modifyFailure
48 , prependFailure
49 , prependFailure
49 , parserThrowError
50 , parserThrowError
@@ -94,7 +95,6 @@ import Data.HashMap.Strict (HashMap)
94import Data.Hashable (Hashable(..))
95import Data.Hashable (Hashable(..))
95import Data.List (intercalate)
96import Data.List (intercalate)
96import Data.Scientific (Scientific)
97import Data.Scientific (Scientific)
97−import Data.Semigroup (Semigroup((<>)))
98import Data.String (IsString(..))
98import Data.String (IsString(..))
99import Data.Text (Text, pack, unpack)
99import Data.Text (Text, pack, unpack)
100import Data.Time (UTCTime)
100import Data.Time (UTCTime)
@@ -342,6 +342,10 @@ instance Monoid (Parser a) where
342 mappend = (<>)
342 mappend = (<>)
343 {-# INLINE mappend #-}
343 {-# INLINE mappend #-}
344
344
345+-- | Raise a parsing failure with some custom message.
346+parseFail :: String -> Parser a
347+parseFail = fail
348+
345apP :: Parser (a -> b) -> Parser a -> Parser b
349apP :: Parser (a -> b) -> Parser a -> Parser b
346apP d e = do
350apP d e = do
347 b <- d
351 b <- d
@@ -421,6 +425,9 @@ instance TH.Lift Value where
421 where a' = V.toList a
425 where a' = V.toList a
422 lift (Object o) = [| Object (H.fromList . map (first pack) $ o') |]
426 lift (Object o) = [| Object (H.fromList . map (first pack) $ o') |]
423 where o' = map (first unpack) . H.toList $ o
427 where o' = map (first unpack) . H.toList $ o
428+#if MIN_VERSION_template_haskell(2,16,0)
429+ liftTyped = TH.unsafeTExpCoerce . TH.lift
430+#endif
424
431
425-- | The empty array.
432-- | The empty array.
426emptyArray :: Value
433emptyArray :: Value
@@ -581,41 +588,53 @@ data Options = Options
581 -- the constructor tag. If 'False' the encoding will always
588 -- the constructor tag. If 'False' the encoding will always
582 -- follow the `sumEncoding`.
589 -- follow the `sumEncoding`.
583 , omitNothingFields :: Bool
590 , omitNothingFields :: Bool
584− -- ^ If 'True' record fields with a 'Nothing' value will be
591+ -- ^ If 'True', record fields with a 'Nothing' value will be
585− -- omitted from the resulting object. If 'False' the resulting
592+ -- omitted from the resulting object. If 'False', the resulting
586 -- object will include those fields mapping to @null@.
593 -- object will include those fields mapping to @null@.
587 --
594 --
595+ -- Note that this /does not/ affect parsing: 'Maybe' fields are
596+ -- optional regardless of the value of 'omitNothingFields', subject
597+ -- to the note below.
598+ --
588 -- === Note
599 -- === Note
589 --
600 --
590 -- Setting 'omitNothingFields' to 'True' only affects fields which are of
601 -- Setting 'omitNothingFields' to 'True' only affects fields which are of
591− -- type 'Maybe' /uniformly/ in the 'ToJSON' or 'FromJSON' instance. In
602+ -- type 'Maybe' /uniformly/ in the 'ToJSON' instance.
592− -- particular, if the type of a field is declared as a type variable, it
603+ -- In particular, if the type of a field is declared as a type variable, it
593 -- will not be omitted from the JSON object, unless the field is
604 -- will not be omitted from the JSON object, unless the field is
594 -- specialized upfront in the instance.
605 -- specialized upfront in the instance.
595 --
606 --
607+ -- The same holds for 'Maybe' fields being optional in the 'FromJSON' instance.
608+ --
596 -- ==== __Example__
609 -- ==== __Example__
597 --
610 --
598 -- The generic instance for the following type @Fruit@ depends on whether
611 -- The generic instance for the following type @Fruit@ depends on whether
599 -- the instance head is @Fruit a@ or @Fruit (Maybe a)@.
612 -- the instance head is @Fruit a@ or @Fruit (Maybe a)@.
600 --
613 --
601 -- @
614 -- @
602− -- data Fruit a =
615+ -- data Fruit a = Fruit
603 -- { apples :: a -- A field whose type is a type variable.
616 -- { apples :: a -- A field whose type is a type variable.
604 -- , oranges :: 'Maybe' Int
617 -- , oranges :: 'Maybe' Int
605− -- }
618+ -- } deriving 'Generic'
606− --
607− -- options :: 'Options'
608− -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
609 --
619 --
610 -- -- apples required, oranges optional
620 -- -- apples required, oranges optional
611 -- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)).
621 -- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)).
612− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a) where
622+ -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a)
613− -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
614 --
623 --
615 -- -- apples optional, oranges optional
624 -- -- apples optional, oranges optional
616 -- -- In this instance, the field apples is uniformly of type ('Maybe' a).
625 -- -- In this instance, the field apples is uniformly of type ('Maybe' a).
617− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a)) where
626+ -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a))
618− -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
627+ --
628+ -- options :: 'Options'
629+ -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
630+ --
631+ -- -- apples always present in the output, oranges is omitted if 'Nothing'
632+ -- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit a) where
633+ -- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options
634+ --
635+ -- -- both apples and oranges are omitted if 'Nothing'
636+ -- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit ('Maybe' a)) where
637+ -- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options
619 -- @
638 -- @
620 , sumEncoding :: SumEncoding
639 , sumEncoding :: SumEncoding
621 -- ^ Specifies how to encode constructors of a sum datatype.
640 -- ^ Specifies how to encode constructors of a sum datatype.
Data/Aeson/Types/ToJSON.hs−1
@@ -79,7 +79,6 @@ import Data.Functor.Sum (Sum(..))
79import Data.Int (Int16, Int32, Int64, Int8)
79import Data.Int (Int16, Int32, Int64, Int8)
80import Data.List (intersperse)
80import Data.List (intersperse)
81import Data.List.NonEmpty (NonEmpty(..))
81import Data.List.NonEmpty (NonEmpty(..))
82−import Data.Semigroup ((<>))
83import Data.Proxy (Proxy(..))
82import Data.Proxy (Proxy(..))
84import Data.Ratio (Ratio, denominator, numerator)
83import Data.Ratio (Ratio, denominator, numerator)
85import Data.Scientific (Scientific)
84import Data.Scientific (Scientific)
aeson.cabal+5−5
@@ -1,5 +1,5 @@
1name: aeson
1name: aeson
2−version: 1.4.6.0
2+version: 1.4.7.1
3license: BSD3
3license: BSD3
4license-file: LICENSE
4license-file: LICENSE
5category: Text, Web, JSON
5category: Text, Web, JSON
@@ -106,8 +106,8 @@ library
106 bytestring >= 0.10.4.0 && < 0.11,
106 bytestring >= 0.10.4.0 && < 0.11,
107 containers >= 0.5.5.1 && < 0.7,
107 containers >= 0.5.5.1 && < 0.7,
108 deepseq >= 1.3.0.0 && < 1.5,
108 deepseq >= 1.3.0.0 && < 1.5,
109− ghc-prim >= 0.2 && < 0.6,
109+ ghc-prim >= 0.2 && < 0.7,
110− template-haskell >= 2.9.0.0 && < 2.16,
110+ template-haskell >= 2.9.0.0 && < 2.17,
111 text >= 1.2.3.0 && < 1.3,
111 text >= 1.2.3.0 && < 1.3,
112 time >= 1.4 && < 1.10
112 time >= 1.4 && < 1.10
113
113
@@ -116,8 +116,8 @@ library
116
116
117 -- Compat
117 -- Compat
118 build-depends:
118 build-depends:
119− base-compat >= 0.9.1 && < 0.12,
119+ base-compat-batteries >= 0.10.0 && < 0.12,
120− time-compat >= 1.9.2.2 && < 1.10
120+ time-compat >= 1.9.2.2 && < 1.10
121
121
122 if !impl(ghc >= 8.6)
122 if !impl(ghc >= 8.6)
123 build-depends:
123 build-depends:
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+3−18
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE NoImplicitPrelude #-}
3-- |
2-- |
4-- Module: Data.Aeson.Internal.Time
3-- Module: Data.Aeson.Internal.Time
@@ -19,24 +18,10 @@ module Data.Attoparsec.Time.Internal
19
18
20import Prelude.Compat
19import Prelude.Compat
21
20
21+import Data.Fixed (Fixed(MkFixed), Pico)
22import Data.Int (Int64)
22import Data.Int (Int64)
23−import Data.Time
23+import Data.Time (TimeOfDay(..))
24−import Unsafe.Coerce (unsafeCoerce)
25−
26−#if MIN_VERSION_time(1,6,0)
27−
28−import Data.Time.Clock (diffTimeToPicoseconds)
24+import Data.Time.Clock.Compat
29−
30−#endif
31−
32−import Data.Fixed (Pico, Fixed(MkFixed))
33−
34−#if !MIN_VERSION_time(1,6,0)
35−
36−diffTimeToPicoseconds :: DiffTime -> Integer
37−diffTimeToPicoseconds = unsafeCoerce
38−
39−#endif
40
25
41toPico :: Integer -> Pico
26toPico :: Integer -> Pico
42toPico = MkFixed
27toPico = MkFixed
attoparsec-iso8601/attoparsec-iso8601.cabal+5−4
@@ -35,11 +35,12 @@ library
35 Data.Attoparsec.Time.Internal
35 Data.Attoparsec.Time.Internal
36 Data.Attoparsec.Time
36 Data.Attoparsec.Time
37 build-depends:
37 build-depends:
38− attoparsec >= 0.13.0.1,
38+ attoparsec >= 0.13.0.1 && < 0.14.0.0,
39 base >= 4.7 && < 5,
39 base >= 4.7 && < 5,
40− base-compat >= 0.9.1 && < 0.12,
40+ base-compat-batteries >= 0.10.0 && < 0.12,
41+ time-compat >= 1.9.2.2 && < 1.10,
41− text >= 1.1.1.0,
42+ text >= 1.1.1.0 && < 1.3.0.0,
42− time >= 1.1.1.4
43+ time >= 1.1.1.4 && < 1.11
43
44
44 if flag(fast)
45 if flag(fast)
45 ghc-options: -O0
46 ghc-options: -O0
changelog.md+61−2
@@ -1,9 +1,68 @@
1For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
1For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
2
2
3−### upcoming
3+#### 1.4.7.1
4+
5+* GHC 8.10 compatibility, thanks to Ryan Scott.
6+
7+### 1.4.7.0
8+
9+Long overdue release (once again), so there's quite a bit of stuff
10+included even though it's a "minor" release. Big thanks to all the
11+contributors, the project would not exist without you!
12+
13+Special thanks to Oleg Grenrus and Xia Li-Yao for reviewing tons
14+of stuff.
15+
16+New stuff:
17+
18+* Add `rejectUnknownFields` to Options which rejects unknown fields on
19+ deserialization. Useful to find errors during development, but
20+ enabling this should be considered a breaking change as previously
21+ accepted inputs may now be rejected. Thanks to rmanne.
22+
23+```
24+instance FromJSON Foo where
25+ parseJSON = gParseJSON defaultOptions { rejectUnknownFields = True }
26+```
4
27
5* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
28* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
6− standard `{numerator=..., denumerator=...}` encoding.
29+ standard `{numerator=..., denumerator=...}` encoding. Thanks to
30+ Aleksey Khudyakov.
31+
32+* Add more information to parse errors, including a sample of the
33+ surrounding text. Hopefully this will lead to less "Failed to read:
34+ satisfy" confusion! Thanks to Sasha Bogicevic. We expect some
35+ downstream test suites to break because of this, apologies in
36+ advance. Hopefully you will like the improvement anyway :-)
37+
38+* Add `parseFail` to `Data.Aeson.Types`. `parseFail = fail` but
39+ doesn't require users to know about `MonadFail`. Thanks to Colin
40+ Woodbury.
41+
42+* Make Template Haskell type family detection smarter when deriving
43+ `ToJSON1` instances, thanks to Ryan Scott.
44+
45+* Optimize string parsing for the common case of strings without
46+ escapes, thanks to Yuras.
47+
48+
49+Misc:
50+
51+* Clean up compiler warnings and switch from base-compat to
52+ base-compat-batteries. Thanks to Colin Woodbury & Oleg Grenrus.
53+
54+* Clarification & fixes to documentation regarding treatment of Maybe fields, thanks to Roman Cheplyaka.
55+
56+* Add documentation for internal development workflows. Thanks to Guru
57+ Devanla.
58+
59+* Drop support for GHC < 7.8. We've chosen to support older GHCs as
60+ long as it doesn't prevent us from adding new features, but now it
61+ does! Thanks to Oleg Grenrus for the patch.
62+
63+* Allow generic-deriving 1.13 in test suite.
64+
65+* Some DRY fixes thanks to Mark Fajkus.
7
66
8### 1.4.6.0
67### 1.4.6.0
9
68
tests/UnitTests.hs+8
@@ -5,6 +5,7 @@
5{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TemplateHaskell #-}
7{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE QuasiQuotes #-}
9{-# LANGUAGE QuasiQuotes #-}
9
10
10-- For Data.Aeson.Types.camelTo
11-- For Data.Aeson.Types.camelTo
@@ -747,6 +748,11 @@ bigNaturalKeyDecoding =
747 (Left "Error in $['1e2000']: found a number with exponent 2000, but it must not be greater than 1024")
748 (Left "Error in $['1e2000']: found a number with exponent 2000, but it must not be greater than 1024")
748 ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
749 ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
749
750
751+-- A regression test for: https://github.com/bos/aeson/issues/757
752+type family Fam757 :: * -> *
753+type instance Fam757 = Maybe
754+newtype Newtype757 a = MkNewtype757 (Fam757 a)
755+
750deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
756deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
751
757
752deriveToJSON defaultOptions ''Foo
758deriveToJSON defaultOptions ''Foo
@@ -758,3 +764,5 @@ deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFields
758deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
764deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
759deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
765deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
760deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
766deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
767+
768+deriveToJSON1 defaultOptions ''Newtype757
travis/install.sh+8−3
@@ -50,9 +50,14 @@ case $BUILD in
50 fi
50 fi
51 ;;
51 ;;
52 cabal2)
52 cabal2)
53− cabal v2-update -v
53+ cabal v2-update -v
54 sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
54 sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
55
55
56− cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
56+ cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
57− ;;
57+ ;;
58+ attoparsec-iso8601)
59+ cd attoparsec-iso8601
60+ cabal v2-update -v
61+ sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
62+ cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
58esac
63esac
travis/script.sh+5−1
@@ -19,7 +19,11 @@ case $BUILD in
19 SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
19 SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
20 (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
20 (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
21 ;;
21 ;;
22− cabal2)
22+ cabal2)
23+ cabal v2-build --enable-tests --enable-benchmarks
24+ cabal v2-test --enable-tests --enable-benchmarks
25+ ;;
26+ attoparsec-iso8601)
23 cabal v2-build --enable-tests --enable-benchmarks
27 cabal v2-build --enable-tests --enable-benchmarks
24 cabal v2-test --enable-tests --enable-benchmarks
28 cabal v2-test --enable-tests --enable-benchmarks
25 ;;
29 ;;