我试图在snap中使用runGHC来过滤掉只能编译的代码。但是,我正在使用tryIO,但是当有编译错误而不是只返回一个空字符串时,我的webhandler仍会抛出异常。
import Exception (tryIO)
...
runOnFileName :: String -> IO (String)
runOnFileName inp = do
res <- sanitizeSource inp
case res of
Just (code, _, _, _) -> return $ ppr code
Nothing -> return ""
sanitizeSourceString :: String -> String -> IO (String)
sanitizeSourceString fn contents = do
tmpdir <- getTemporaryDirectory
let tmp = tmpdir </> fn ++ ".hs"
exists <- doesFileExist tmp
unless exists $ writeFile tmp $ contents
runOnFileName tmp
sanitizeSource :: String -> IO (Maybe RenamedSource)
sanitizeSource inp = do
runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
let dflags' = foldl xopt_set dflags
[Opt_Cpp, Opt_ImplicitPrelude, Opt_MagicHash]
setSessionDynFlags dflags
target <- guessTarget inp Nothing
setTargets [target]
load LoadAllTargets
modSum <- getModSummary $ mkModuleName "Main"
p <- parseModule modSum
t <- typecheckModule p
d <- desugarModule t
return $ renamedSource d
... in my handler...
eitherSan <- liftIO $ tryIO $ sanitizeSourceString (T.unpack uuid) (fromMaybe "" content)
let sanitized = case eitherSan of
Left _ -> ""
Right r -> r
但是,如果我传递了无法编译的“内容”,我的处理程序将失败,并带有
A web handler threw an exception. Details Parse error: naked expression at top level
或编译器错误。我认为tryIO
应该捕获异常。
答案 0 :(得分:1)
Daniel Fischer关于你使用tryIO
哪个问题的关键问题。也就是说,要么tryIO
只捕获IO异常等,在这种情况下你应该使用Control.Exception
的组合子,或者tryIO
捕获多态异常,但是你还没有指定你正在捕捉它所以它默认为一些愚蠢的东西(比如单位)你需要更明确,或者,不知何故,异常没有被抛出但是只有一个错误值隐藏在sanitizeString返回的thunk中,这需要在evaluate
块内强制(通过调用try
)。
答案 1 :(得分:0)
您需要使用SourceError
来抓住GhcApiError
,SomeException
和gcatch
。这是您的代码的独立版本:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad
import GHC
import GHC.Paths ( libdir )
import DynFlags
import Outputable
import System.FilePath
import System.Directory
import Control.Exception (SomeException)
import HscTypes (SourceError, GhcApiError)
runOnFileName :: String -> IO (String)
runOnFileName inp = do
res <- sanitizeSource inp
case res of
Just (code, _, _, _) -> return $ showSDoc tracingDynFlags (ppr code)
Nothing -> return ""
sanitizeSourceString :: String -> String -> IO (String)
sanitizeSourceString fn contents = do
tmpdir <- getTemporaryDirectory
let tmp = tmpdir </> fn ++ ".hs"
exists <- doesFileExist tmp
unless exists $ writeFile tmp $ contents
runOnFileName tmp
sanitizeSource :: String -> IO (Maybe RenamedSource)
sanitizeSource inp = (sanitizeSource' inp)
`gcatch` (\(e :: SourceError) -> return Nothing)
`gcatch` (\(g :: GhcApiError) -> return Nothing)
`gcatch` (\(se :: SomeException) -> return Nothing)
sanitizeSource' :: String -> IO (Maybe RenamedSource)
sanitizeSource' inp = do
runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
let dflags' = foldl xopt_set dflags
[Opt_Cpp, Opt_ImplicitPrelude, Opt_MagicHash]
setSessionDynFlags dflags
target <- guessTarget inp Nothing
setTargets [target]
load LoadAllTargets
modSum <- getModSummary $ mkModuleName "Main"
p <- parseModule modSum
t <- typecheckModule p
d <- desugarModule t
return $ renamedSource d
eg1 = "module Main where\n\nf x = x + 1\n\nmain = undefined\n" -- will compile
eg2 = "module Main where\n\nf = x + 1\n\nmain = undefined\n" -- won't compile
示例会话:
*Main> :!rm -v /tmp/*hs
解析ok的代码:
*Main> r1 <- sanitizeSourceString "fn_eg1" eg1
*Main> r1
"Main.f x = x GHC.Num.+ 1\nMain.main = GHC.Err.undefined"
现在,代码无法解析。打印错误,但r2
确实得到空字符串,
所以异常被正确捕获:
*Main> r2 <- sanitizeSourceString "fn_eg2" eg2
/tmp/fn_eg2.hs:3:5: Not in scope: `x'
*Main> r2
""