language-thrift 0.8.0.1 → 0.8.0.2
raw patch · 6 files changed
+143/−30 lines, 6 filesdep +containersPVP ok
version bump matches the API change (PVP)
Dependencies added: containers
API changes (from Hackage documentation)
Files
- CHANGES.md +5/−0
- language-thrift.cabal +20/−10
- src/Language/Thrift/Internal/Reserved.hs +59/−0
- src/Language/Thrift/Parser.hs +16/−4
- test/Language/Thrift/Arbitrary.hs +36/−16
- test/Language/Thrift/ParserSpec.hs +7/−0
CHANGES.md view
@@ -1,3 +1,8 @@+0.8.0.2 (2016-08-31)+====================++- Disallow reserved keywords from being used as identifier names.+ 0.8.0.1 (2016-05-24) ====================
language-thrift.cabal view
@@ -1,9 +1,9 @@--- This file has been generated from package.yaml by hpack version 0.13.0.+-- This file has been generated from package.yaml by hpack version 0.14.1. -- -- see: https://github.com/sol/hpack name: language-thrift-version: 0.8.0.1+version: 0.8.0.2 cabal-version: >= 1.10 build-type: Simple license: BSD3@@ -32,21 +32,23 @@ library hs-source-dirs: src+ ghc-options: -Wall exposed-modules: Language.Thrift.Parser Language.Thrift.Pretty Language.Thrift.Types+ other-modules:+ Language.Thrift.Internal.Reserved+ Language.Thrift.Internal.Types+ Paths_language_thrift+ default-language: Haskell2010 build-depends: base >= 4.7 && < 5, ansi-wl-pprint >= 0.6 && < 0.7,+ containers >= 0.5 && < 0.6, megaparsec >= 4.0 && < 5.0, text >= 1.2, transformers- default-language: Haskell2010- other-modules:- Language.Thrift.Internal.Types- Paths_language_thrift- ghc-options: -Wall test-suite spec type: exitcode-stdio-1.0@@ -54,19 +56,27 @@ build-depends: base >= 4.7 && < 5, ansi-wl-pprint >= 0.6 && < 0.7,+ containers >= 0.5 && < 0.6, megaparsec >= 4.0 && < 5.0, text >= 1.2,+ transformers, hspec >= 2.0, hspec-discover >= 2.1, language-thrift, QuickCheck >= 2.5- default-language: Haskell2010- hs-source-dirs:- test other-modules:+ Language.Thrift.Internal.Reserved+ Language.Thrift.Internal.Types+ Language.Thrift.Parser+ Language.Thrift.Pretty+ Language.Thrift.Types Language.Thrift.Arbitrary Language.Thrift.ParserSpec Language.Thrift.TypesSpec Spec TestUtils+ default-language: Haskell2010+ hs-source-dirs:+ src,+ test ghc-options: -Wall
+ src/Language/Thrift/Internal/Reserved.hs view
@@ -0,0 +1,59 @@+-- |+-- Module : Language.Thrift.Internal.Reserved+-- Copyright : (c) Abhinav Gupta 2016+-- License : BSD3+--+-- Maintainer : Abhinav Gupta <mail@abhinavg.net>+-- Stability : experimental+--+-- This module provides information about reserved Thrift identifiers.+module Language.Thrift.Internal.Reserved+ ( isReserved+ ) where++import Data.Set (Set)+import qualified Data.Set as Set++isReserved :: String -> Bool+isReserved = (`Set.member` reservedKeywords)++reservedKeywords :: Set String+reservedKeywords = Set.fromList+ [ "include"+ , "namespace"+ , "cpp_namespace"+ , "php_namespace"+ , "py_module"+ , "perl_package"+ , "ruby_namespace"+ , "java_package"+ , "cocoa_package"+ , "csharp_namespace"+ , "typedef"+ , "enum"+ , "struct"+ , "union"+ , "exception"+ , "required"+ , "optional"+ , "senum"+ , "const"+ , "string"+ , "binary"+ , "slist"+ , "bool"+ , "byte"+ , "i8"+ , "i16"+ , "i32"+ , "i64"+ , "double"+ , "map"+ , "set"+ , "list"+ , "service"+ , "extends"+ , "oneway"+ , "void"+ , "throws"+ ]
src/Language/Thrift/Parser.hs view
@@ -71,6 +71,8 @@ import qualified Text.Megaparsec as P import qualified Text.Megaparsec.Lexer as PL +import Language.Thrift.Internal.Reserved (isReserved)+ import qualified Language.Thrift.Types as T -- | Keeps track of the last docstring seen by the system so that we can@@ -239,13 +241,21 @@ colon = symbolic ':' equals = symbolic '=' +-- | errorUnlessReserved ensures that the given identifier is in the+-- reservedKeywords list. If it's not, we have a bug and we should crash.+errorUnlessReserved :: Monad m => String -> m ()+errorUnlessReserved name =+ unless (isReserved name) $+ error ("reserved called with unreserved identifier " ++ show name)+ -- | Parses a reserved identifier and adds it to the collection of known -- reserved keywords. reserved :: P.Stream s Char => String -> Parser s ()-reserved name = P.label name $ token $ P.try $ do- void (P.string name)- P.notFollowedBy (P.alphaNumChar <|> P.oneOf "_.")-+reserved name =+ errorUnlessReserved name >>+ P.label name $ token $ P.try $ do+ void (P.string name)+ P.notFollowedBy (P.alphaNumChar <|> P.oneOf "_.") -- | A string literal. @"hello"@ literal :: P.Stream s Char => Parser s Text@@ -267,6 +277,8 @@ name <- (:) <$> (P.letterChar <|> P.char '_') <*> many (P.alphaNumChar <|> P.oneOf "_.")+ when (isReserved name) $+ P.unexpected name return (Text.pack name)
test/Language/Thrift/Arbitrary.hs view
@@ -17,6 +17,8 @@ import qualified Data.Text as Text +import Language.Thrift.Internal.Reserved (isReserved)+ import qualified Language.Thrift.Types as T #ifdef MIN_VERSION_QuickCheck@@ -64,6 +66,15 @@ shrink (Docstring t) = Docstring <$> shrink t +newtype Identifier = Identifier { getIdentifier :: Text }+ deriving (Show, Typeable, Generic)++instance Arbitrary Identifier where+ arbitrary = Identifier <$> arbitrary `suchThat` (not . isReserved . Text.unpack)++ shrink (Identifier t) =+ [Identifier t' | t' <- shrink t, not (isReserved (Text.unpack t'))]+ ------------------------------------------------------------------------------ instance Arbitrary (T.Program ()) where@@ -83,7 +94,11 @@ instance Arbitrary (T.Namespace ()) where shrink = genericShrink- arbitrary = T.Namespace <$> elements scopes <*> arbitrary <*> pure ()+ arbitrary =+ T.Namespace+ <$> elements scopes+ <*> (getIdentifier <$> arbitrary)+ <*> pure () where scopes = ["*", "py", "rb", "java", "hs", "cpp"] @@ -101,7 +116,7 @@ arbitrary = T.Const <$> arbitrary- <*> arbitrary+ <*> (getIdentifier <$> arbitrary) <*> arbitrary <*> (getDocstring <$> arbitrary) <*> pure ()@@ -110,8 +125,11 @@ shrink = genericShrink arbitrary = T.Service- <$> arbitrary- <*> arbitrary+ <$> (getIdentifier <$> arbitrary)+ <*> frequency+ [ (1, return Nothing)+ , (3, Just . getIdentifier <$> arbitrary)+ ] <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -123,7 +141,7 @@ T.Function <$> arbitrary <*> halfSize arbitrary- <*> arbitrary+ <*> (getIdentifier <$> arbitrary) <*> halfSize arbitrary <*> halfSize arbitrary <*> halfSize arbitrary@@ -145,7 +163,7 @@ shrink = genericShrink arbitrary = T.Typedef <$> arbitrary- <*> arbitrary+ <*> (getIdentifier <$> arbitrary) <*> halfSize arbitrary <*> (getDocstring <$> arbitrary) <*> pure ()@@ -153,7 +171,7 @@ instance Arbitrary (T.Enum ()) where shrink = genericShrink arbitrary = T.Enum- <$> arbitrary+ <$> (getIdentifier <$> arbitrary) <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -163,7 +181,7 @@ shrink = genericShrink arbitrary = T.EnumDef- <$> arbitrary+ <$> (getIdentifier <$> arbitrary) <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -172,7 +190,7 @@ instance Arbitrary (T.Struct ()) where shrink = genericShrink arbitrary = T.Struct- <$> arbitrary+ <$> (getIdentifier <$> arbitrary) <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -181,7 +199,7 @@ instance Arbitrary (T.Union ()) where shrink = genericShrink arbitrary = T.Union- <$> arbitrary+ <$> (getIdentifier <$> arbitrary) <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -190,7 +208,7 @@ instance Arbitrary (T.Exception ()) where shrink = genericShrink arbitrary = T.Exception- <$> arbitrary+ <$> (getIdentifier <$> arbitrary) <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -199,7 +217,7 @@ instance Arbitrary (T.Senum ()) where shrink = genericShrink arbitrary = T.Senum- <$> arbitrary+ <$> (getIdentifier <$> arbitrary) <*> arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -212,7 +230,7 @@ <$> (fmap getPositive <$> arbitrary) <*> arbitrary <*> halfSize arbitrary- <*> arbitrary+ <*> (getIdentifier <$> arbitrary) <*> halfSize arbitrary <*> halfSize arbitrary <*> (getDocstring <$> arbitrary)@@ -221,7 +239,8 @@ instance Arbitrary (T.TypeReference ()) where shrink = genericShrink arbitrary = oneof- [ T.DefinedType <$> arbitrary <*> pure ()+ [ T.DefinedType <$> (getIdentifier <$> arbitrary)+ <*> pure () , halfSize $ T.StringType <$> arbitrary <*> pure () , halfSize $ T.BinaryType <$> arbitrary <*> pure () , halfSize $ T.SListType <$> arbitrary <*> pure ()@@ -242,7 +261,7 @@ instance Arbitrary T.TypeAnnotation where shrink = genericShrink- arbitrary = T.TypeAnnotation <$> arbitrary <*> arbitrary+ arbitrary = T.TypeAnnotation <$> (getIdentifier <$> arbitrary) <*> arbitrary newtype BasicConstValue = BasicConstValue {@@ -256,7 +275,8 @@ [ T.ConstFloat <$> choose (0.0, 10000.0) <*> pure () , T.ConstInt <$> arbitrary <*> pure () , T.ConstLiteral <$> arbitrary <*> pure ()- , T.ConstIdentifier <$> arbitrary <*> pure ()+ , T.ConstIdentifier <$> (getIdentifier <$> arbitrary)+ <*> pure () ] -- | newtype wrapper around const values so that we're not generating lists
test/Language/Thrift/ParserSpec.hs view
@@ -80,6 +80,13 @@ , "something_else<foo>" ] + it "cannot parse reserved keywords in names" $+ parseFailureCases P.program+ [ "enum struct {}"+ , "strut Foo { 1: required string service }"+ ]++ parseFailureCases :: Show a => Parser a -> [String] -> Expectation parseFailureCases p = mapM_ (p `shouldNotParse`)