Add check of syntactic validity of output
parent
bc09b21473
commit
d444c6e386
|
@ -139,6 +139,7 @@ executable brittany
|
||||||
, safe
|
, safe
|
||||||
, filepath >=1.4.1.0 && <1.5
|
, filepath >=1.4.1.0 && <1.5
|
||||||
, either
|
, either
|
||||||
|
, ghc-boot-th
|
||||||
}
|
}
|
||||||
hs-source-dirs: src-brittany
|
hs-source-dirs: src-brittany
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
|
@ -31,6 +31,9 @@ import qualified System.Exit
|
||||||
import qualified System.Directory as Directory
|
import qualified System.Directory as Directory
|
||||||
import qualified System.FilePath.Posix as FilePath
|
import qualified System.FilePath.Posix as FilePath
|
||||||
|
|
||||||
|
import qualified DynFlags as GHC
|
||||||
|
import qualified GHC.LanguageExtensions.Type as GHC
|
||||||
|
|
||||||
import Paths_brittany
|
import Paths_brittany
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,16 +108,35 @@ mainCmdParser = do
|
||||||
& _options_ghc
|
& _options_ghc
|
||||||
& runIdentity
|
& runIdentity
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
|
let cppMode = config
|
||||||
|
& _conf_errorHandling
|
||||||
|
& _econf_CPPMode
|
||||||
|
& runIdentity
|
||||||
|
& Semigroup.getLast
|
||||||
|
let cppCheckFunc dynFlags = if GHC.xopt GHC.Cpp dynFlags
|
||||||
|
then case cppMode of
|
||||||
|
CPPModeAbort -> do
|
||||||
|
return $ Left "Encountered -XCPP. Aborting."
|
||||||
|
CPPModeWarn -> do
|
||||||
|
putStrErrLn
|
||||||
|
$ "Warning: Encountered -XCPP."
|
||||||
|
++ " Be warned that -XCPP is not supported and that"
|
||||||
|
++ " brittany cannot check that its output is syntactically"
|
||||||
|
++ " valid in its presence."
|
||||||
|
return $ Right True
|
||||||
|
CPPModeNowarn ->
|
||||||
|
return $ Right True
|
||||||
|
else return $ Right False
|
||||||
parseResult <- case inputPathM of
|
parseResult <- case inputPathM of
|
||||||
Nothing -> parseModuleFromString ghcOptions "stdin"
|
Nothing -> parseModuleFromString ghcOptions "stdin" cppCheckFunc
|
||||||
=<< System.IO.hGetContents System.IO.stdin
|
=<< System.IO.hGetContents System.IO.stdin
|
||||||
Just p -> parseModule ghcOptions p
|
Just p -> parseModule ghcOptions p cppCheckFunc
|
||||||
case parseResult of
|
case parseResult of
|
||||||
Left left -> do
|
Left left -> do
|
||||||
putStrErrLn "parse error:"
|
putStrErrLn "parse error:"
|
||||||
printErr left
|
printErr left
|
||||||
System.Exit.exitWith (System.Exit.ExitFailure 60)
|
System.Exit.exitWith (System.Exit.ExitFailure 60)
|
||||||
Right (anns, parsedSource) -> do
|
Right (anns, parsedSource, hasCPP) -> do
|
||||||
when (config & _conf_debug .> _dconf_dump_ast_full .> confUnpack) $ do
|
when (config & _conf_debug .> _dconf_dump_ast_full .> confUnpack) $ do
|
||||||
let val = printTreeWithCustom 100 (customLayouterF anns) parsedSource
|
let val = printTreeWithCustom 100 (customLayouterF anns) parsedSource
|
||||||
trace ("---- ast ----\n" ++ show val) $ return ()
|
trace ("---- ast ----\n" ++ show val) $ return ()
|
||||||
|
@ -125,15 +147,20 @@ mainCmdParser = do
|
||||||
-- let out = do
|
-- let out = do
|
||||||
-- decl <- someDecls
|
-- decl <- someDecls
|
||||||
-- ExactPrint.exactPrint decl anns
|
-- ExactPrint.exactPrint decl anns
|
||||||
let (errsWarns, outLText) = pPrintModule config anns parsedSource
|
(errsWarns, outLText) <- if hasCPP
|
||||||
|
then return $ pPrintModule config anns parsedSource
|
||||||
|
else pPrintModuleAndCheck config anns parsedSource
|
||||||
let customErrOrder LayoutWarning{} = 0 :: Int
|
let customErrOrder LayoutWarning{} = 0 :: Int
|
||||||
customErrOrder LayoutErrorUnusedComment{} = 1
|
customErrOrder LayoutErrorOutputCheck{} = 1
|
||||||
customErrOrder LayoutErrorUnknownNode{} = 2
|
customErrOrder LayoutErrorUnusedComment{} = 2
|
||||||
|
customErrOrder LayoutErrorUnknownNode{} = 3
|
||||||
when (not $ null errsWarns) $ do
|
when (not $ null errsWarns) $ do
|
||||||
let groupedErrsWarns = Data.List.Extra.groupOn customErrOrder
|
let groupedErrsWarns = Data.List.Extra.groupOn customErrOrder
|
||||||
$ List.sortOn customErrOrder
|
$ List.sortOn customErrOrder
|
||||||
$ errsWarns
|
$ errsWarns
|
||||||
groupedErrsWarns `forM_` \case
|
groupedErrsWarns `forM_` \case
|
||||||
|
(LayoutErrorOutputCheck{}:_) -> do
|
||||||
|
putStrErrLn $ "ERROR: brittany pretty printer returned syntactically invalid result."
|
||||||
uns@(LayoutErrorUnknownNode{}:_) -> do
|
uns@(LayoutErrorUnknownNode{}:_) -> do
|
||||||
putStrErrLn $ "ERROR: encountered unknown syntactical constructs:"
|
putStrErrLn $ "ERROR: encountered unknown syntactical constructs:"
|
||||||
uns `forM_` \case
|
uns `forM_` \case
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
module Language.Haskell.Brittany
|
module Language.Haskell.Brittany
|
||||||
( parsePrintModule
|
( parsePrintModule
|
||||||
, pPrintModule
|
, pPrintModule
|
||||||
|
, pPrintModuleAndCheck
|
||||||
-- re-export from utils:
|
-- re-export from utils:
|
||||||
, parseModule
|
, parseModule
|
||||||
, parseModuleFromString
|
, parseModuleFromString
|
||||||
|
@ -79,6 +80,22 @@ pPrintModule conf anns parsedModule =
|
||||||
-- debugStrings `forM_` \s ->
|
-- debugStrings `forM_` \s ->
|
||||||
-- trace s $ return ()
|
-- trace s $ return ()
|
||||||
|
|
||||||
|
-- | Additionally checks that the output compiles again, appending an error
|
||||||
|
-- if it does not.
|
||||||
|
pPrintModuleAndCheck
|
||||||
|
:: Config
|
||||||
|
-> ExactPrint.Types.Anns
|
||||||
|
-> GHC.ParsedSource
|
||||||
|
-> IO ([LayoutError], TextL.Text)
|
||||||
|
pPrintModuleAndCheck conf anns parsedModule = do
|
||||||
|
let (errs, output) = pPrintModule conf anns parsedModule
|
||||||
|
parseResult <- ExactPrint.Parsers.parseModuleFromString "output" (TextL.unpack output)
|
||||||
|
let errs' = errs ++ case parseResult of
|
||||||
|
Left{} -> [LayoutErrorOutputCheck]
|
||||||
|
Right{} -> []
|
||||||
|
return (errs', output)
|
||||||
|
|
||||||
|
|
||||||
-- used for testing mostly, currently.
|
-- used for testing mostly, currently.
|
||||||
parsePrintModule
|
parsePrintModule
|
||||||
:: Config
|
:: Config
|
||||||
|
@ -88,17 +105,18 @@ parsePrintModule
|
||||||
parsePrintModule conf filename input = do
|
parsePrintModule conf filename input = do
|
||||||
let inputStr = Text.unpack input
|
let inputStr = Text.unpack input
|
||||||
parseResult <- ExactPrint.Parsers.parseModuleFromString filename inputStr
|
parseResult <- ExactPrint.Parsers.parseModuleFromString filename inputStr
|
||||||
return $ case parseResult of
|
case parseResult of
|
||||||
Left (_, s) -> Left $ "parsing error: " ++ s
|
Left (_, s) -> return $ Left $ "parsing error: " ++ s
|
||||||
Right (anns, parsedModule) ->
|
Right (anns, parsedModule) -> do
|
||||||
let (errs, ltext) = pPrintModule conf anns parsedModule
|
(errs, ltext) <- pPrintModuleAndCheck conf anns parsedModule
|
||||||
in if null errs
|
return $ if null errs
|
||||||
then Right $ TextL.toStrict $ ltext
|
then Right $ TextL.toStrict $ ltext
|
||||||
else
|
else
|
||||||
let errStrs = errs <&> \case
|
let errStrs = errs <&> \case
|
||||||
LayoutErrorUnusedComment str -> str
|
LayoutErrorUnusedComment str -> str
|
||||||
LayoutWarning str -> str
|
LayoutWarning str -> str
|
||||||
LayoutErrorUnknownNode str _ -> str
|
LayoutErrorUnknownNode str _ -> str
|
||||||
|
LayoutErrorOutputCheck -> "Output is not syntactically valid."
|
||||||
in Left $ "pretty printing error(s):\n" ++ List.unlines errStrs
|
in Left $ "pretty printing error(s):\n" ++ List.unlines errStrs
|
||||||
|
|
||||||
-- this approach would for with there was a pure GHC.parseDynamicFilePragma.
|
-- this approach would for with there was a pure GHC.parseDynamicFilePragma.
|
||||||
|
|
|
@ -123,6 +123,7 @@ configParser = do
|
||||||
, _conf_errorHandling = ErrorHandlingConfig
|
, _conf_errorHandling = ErrorHandlingConfig
|
||||||
{ _econf_produceOutputOnErrors = wrapLast $ falseToNothing outputOnErrors
|
{ _econf_produceOutputOnErrors = wrapLast $ falseToNothing outputOnErrors
|
||||||
, _econf_Werror = wrapLast $ falseToNothing wError
|
, _econf_Werror = wrapLast $ falseToNothing wError
|
||||||
|
, _econf_CPPMode = Nothing
|
||||||
}
|
}
|
||||||
, _conf_forward = ForwardOptions
|
, _conf_forward = ForwardOptions
|
||||||
{ _options_ghc = [ optionsGhc & List.unwords & CmdArgs.splitArgs
|
{ _options_ghc = [ optionsGhc & List.unwords & CmdArgs.splitArgs
|
||||||
|
|
|
@ -64,6 +64,7 @@ data ForwardOptionsF f = ForwardOptions
|
||||||
data ErrorHandlingConfigF f = ErrorHandlingConfig
|
data ErrorHandlingConfigF f = ErrorHandlingConfig
|
||||||
{ _econf_produceOutputOnErrors :: f (Semigroup.Last Bool)
|
{ _econf_produceOutputOnErrors :: f (Semigroup.Last Bool)
|
||||||
, _econf_Werror :: f (Semigroup.Last Bool)
|
, _econf_Werror :: f (Semigroup.Last Bool)
|
||||||
|
, _econf_CPPMode :: f (Semigroup.Last CPPMode)
|
||||||
}
|
}
|
||||||
deriving (Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
@ -123,6 +124,8 @@ instance FromJSON IndentPolicy
|
||||||
instance ToJSON IndentPolicy
|
instance ToJSON IndentPolicy
|
||||||
instance FromJSON AltChooser
|
instance FromJSON AltChooser
|
||||||
instance ToJSON AltChooser
|
instance ToJSON AltChooser
|
||||||
|
instance FromJSON CPPMode
|
||||||
|
instance ToJSON CPPMode
|
||||||
|
|
||||||
instance FromJSON (LayoutConfigF Maybe)
|
instance FromJSON (LayoutConfigF Maybe)
|
||||||
instance ToJSON (LayoutConfigF Maybe)
|
instance ToJSON (LayoutConfigF Maybe)
|
||||||
|
@ -181,6 +184,13 @@ data AltChooser = AltChooserSimpleQuick -- always choose last alternative.
|
||||||
-- options having sufficient space.
|
-- options having sufficient space.
|
||||||
deriving (Show, Generic, Data)
|
deriving (Show, Generic, Data)
|
||||||
|
|
||||||
|
data CPPMode = CPPModeAbort -- abort program on seeing -XCPP
|
||||||
|
| CPPModeWarn -- warn about CPP and non-roundtripping in its
|
||||||
|
-- presence.
|
||||||
|
| CPPModeNowarn -- silently allow CPP, if possible (i.e. input is
|
||||||
|
-- file.)
|
||||||
|
deriving (Show, Generic, Data)
|
||||||
|
|
||||||
staticDefaultConfig :: Config
|
staticDefaultConfig :: Config
|
||||||
staticDefaultConfig = Config
|
staticDefaultConfig = Config
|
||||||
{ _conf_debug = DebugConfig
|
{ _conf_debug = DebugConfig
|
||||||
|
@ -208,6 +218,7 @@ staticDefaultConfig = Config
|
||||||
, _conf_errorHandling = ErrorHandlingConfig
|
, _conf_errorHandling = ErrorHandlingConfig
|
||||||
{ _econf_produceOutputOnErrors = coerce False
|
{ _econf_produceOutputOnErrors = coerce False
|
||||||
, _econf_Werror = coerce False
|
, _econf_Werror = coerce False
|
||||||
|
, _econf_CPPMode = coerce CPPModeAbort
|
||||||
}
|
}
|
||||||
, _conf_forward = ForwardOptions
|
, _conf_forward = ForwardOptions
|
||||||
{ _options_ghc = Identity []
|
{ _options_ghc = Identity []
|
||||||
|
@ -247,10 +258,11 @@ instance CZip LayoutConfigF where
|
||||||
(f x7 y7)
|
(f x7 y7)
|
||||||
|
|
||||||
instance CZip ErrorHandlingConfigF where
|
instance CZip ErrorHandlingConfigF where
|
||||||
cZip f (ErrorHandlingConfig x1 x2)
|
cZip f (ErrorHandlingConfig x1 x2 x3)
|
||||||
(ErrorHandlingConfig y1 y2) = ErrorHandlingConfig
|
(ErrorHandlingConfig y1 y2 y3) = ErrorHandlingConfig
|
||||||
(f x1 y1)
|
(f x1 y1)
|
||||||
(f x2 y2)
|
(f x2 y2)
|
||||||
|
(f x3 y3)
|
||||||
|
|
||||||
instance CZip ForwardOptionsF where
|
instance CZip ForwardOptionsF where
|
||||||
cZip f (ForwardOptions x1)
|
cZip f (ForwardOptions x1)
|
||||||
|
|
|
@ -64,7 +64,8 @@ import DataTreePrint
|
||||||
parseModule
|
parseModule
|
||||||
:: [String]
|
:: [String]
|
||||||
-> System.IO.FilePath
|
-> System.IO.FilePath
|
||||||
-> IO (Either String (ExactPrint.Anns, GHC.ParsedSource))
|
-> (GHC.DynFlags -> IO (Either String a))
|
||||||
|
-> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))
|
||||||
parseModule =
|
parseModule =
|
||||||
parseModuleWithCpp ExactPrint.defaultCppOptions ExactPrint.normalLayout
|
parseModuleWithCpp ExactPrint.defaultCppOptions ExactPrint.normalLayout
|
||||||
|
|
||||||
|
@ -74,8 +75,9 @@ parseModuleWithCpp
|
||||||
-> ExactPrint.DeltaOptions
|
-> ExactPrint.DeltaOptions
|
||||||
-> [String]
|
-> [String]
|
||||||
-> System.IO.FilePath
|
-> System.IO.FilePath
|
||||||
-> IO (Either String (ExactPrint.Anns, GHC.ParsedSource))
|
-> (GHC.DynFlags -> IO (Either String a))
|
||||||
parseModuleWithCpp cpp opts args fp =
|
-> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))
|
||||||
|
parseModuleWithCpp cpp opts args fp dynCheck =
|
||||||
ExactPrint.ghcWrapper $ EitherT.runEitherT $ do
|
ExactPrint.ghcWrapper $ EitherT.runEitherT $ do
|
||||||
dflags0 <- lift $ ExactPrint.initDynFlags fp
|
dflags0 <- lift $ ExactPrint.initDynFlags fp
|
||||||
(dflags1, leftover, warnings) <- lift $ GHC.parseDynamicFlagsCmdLine
|
(dflags1, leftover, warnings) <- lift $ GHC.parseDynamicFlagsCmdLine
|
||||||
|
@ -89,17 +91,20 @@ parseModuleWithCpp cpp opts args fp =
|
||||||
$ EitherT.left
|
$ EitherT.left
|
||||||
$ "when parsing ghc flags: encountered warnings: "
|
$ "when parsing ghc flags: encountered warnings: "
|
||||||
++ show (warnings <&> \(L _ s) -> s)
|
++ show (warnings <&> \(L _ s) -> s)
|
||||||
|
x <- EitherT.EitherT $ liftIO $ dynCheck dflags1
|
||||||
res <- lift $ ExactPrint.parseModuleApiAnnsWithCppInternal cpp dflags1 fp
|
res <- lift $ ExactPrint.parseModuleApiAnnsWithCppInternal cpp dflags1 fp
|
||||||
EitherT.hoistEither
|
EitherT.hoistEither
|
||||||
$ either (\(span, err) -> Left $ show span ++ ": " ++ err) Right
|
$ either (\(span, err) -> Left $ show span ++ ": " ++ err)
|
||||||
|
(\(a, m) -> Right (a, m, x))
|
||||||
$ ExactPrint.postParseTransform res opts
|
$ ExactPrint.postParseTransform res opts
|
||||||
|
|
||||||
parseModuleFromString
|
parseModuleFromString
|
||||||
:: [String]
|
:: [String]
|
||||||
-> System.IO.FilePath
|
-> System.IO.FilePath
|
||||||
|
-> (GHC.DynFlags -> IO (Either String a))
|
||||||
-> String
|
-> String
|
||||||
-> IO (Either String (ExactPrint.Anns, GHC.ParsedSource))
|
-> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))
|
||||||
parseModuleFromString args fp str =
|
parseModuleFromString args fp dynCheck str =
|
||||||
ExactPrint.ghcWrapper $ EitherT.runEitherT $ do
|
ExactPrint.ghcWrapper $ EitherT.runEitherT $ do
|
||||||
dflags0 <- lift $ ExactPrint.initDynFlagsPure fp str
|
dflags0 <- lift $ ExactPrint.initDynFlagsPure fp str
|
||||||
(dflags1, leftover, warnings) <-
|
(dflags1, leftover, warnings) <-
|
||||||
|
@ -112,8 +117,10 @@ parseModuleFromString args fp str =
|
||||||
$ EitherT.left
|
$ EitherT.left
|
||||||
$ "when parsing ghc flags: encountered warnings: "
|
$ "when parsing ghc flags: encountered warnings: "
|
||||||
++ show (warnings <&> \(L _ s) -> s)
|
++ show (warnings <&> \(L _ s) -> s)
|
||||||
|
x <- EitherT.EitherT $ liftIO $ dynCheck dflags1
|
||||||
EitherT.hoistEither
|
EitherT.hoistEither
|
||||||
$ either (\(span, err) -> Left $ show span ++ ": " ++ err) Right
|
$ either (\(span, err) -> Left $ show span ++ ": " ++ err)
|
||||||
|
(\(a, m) -> Right (a, m, x))
|
||||||
$ ExactPrint.parseWith dflags1 fp GHC.parseModule str
|
$ ExactPrint.parseWith dflags1 fp GHC.parseModule str
|
||||||
|
|
||||||
-----------
|
-----------
|
||||||
|
|
|
@ -115,9 +115,11 @@ instance Show LayoutState where
|
||||||
-- , _lsettings_initialAnns :: ExactPrint.Anns
|
-- , _lsettings_initialAnns :: ExactPrint.Anns
|
||||||
-- }
|
-- }
|
||||||
|
|
||||||
data LayoutError = LayoutErrorUnusedComment String
|
data LayoutError
|
||||||
|
= LayoutErrorUnusedComment String
|
||||||
| LayoutWarning String
|
| LayoutWarning String
|
||||||
| forall ast . Data.Data.Data ast => LayoutErrorUnknownNode String ast
|
| forall ast . Data.Data.Data ast => LayoutErrorUnknownNode String ast
|
||||||
|
| LayoutErrorOutputCheck
|
||||||
|
|
||||||
data BriSpacing = BriSpacing
|
data BriSpacing = BriSpacing
|
||||||
{ _bs_spacePastLineIndent :: Int -- space in the current,
|
{ _bs_spacePastLineIndent :: Int -- space in the current,
|
||||||
|
|
Loading…
Reference in New Issue