git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Fix #1523, add --json-errors flag.
commit11b2662979ee65d64758e95a0925c6b483800061 authorPhil Freeman <paf31@cantab.net> dateTue 24 Nov 2015, 19:17 (11 years ago) parent948e45d
4 files changed+139−25
psc/JSON.hs+69 psc/Main.hs+37−21 purescript.cabal+5−2 src/Language/PureScript/Errors.hs+28−2
psc/JSON.hsnew file+69
@@ -0,0 +1,69 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Main
4+-- Copyright : (c) 2013-15 Phil Freeman, (c) 2014-15 Gary Burgess
5+-- License : MIT (http://opensource.org/licenses/MIT)
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE TemplateHaskell #-}
16+
17+module JSON where
18+
19+import Prelude ()
20+import Prelude.Compat
21+
22+import qualified Data.Aeson.TH as A
23+
24+import qualified Language.PureScript as P
25+
26+data ErrorPosition = ErrorPosition
27+ { startLine :: Int
28+ , startColumn :: Int
29+ , endLine :: Int
30+ , endColumn :: Int
31+ }
32+
33+data JSONError = JSONError
34+ { position :: Maybe ErrorPosition
35+ , message :: String
36+ , errorCode :: String
37+ , filename :: Maybe String
38+ , moduleName :: Maybe String
39+ }
40+
41+data JSONResult = JSONResult
42+ { warnings :: [JSONError]
43+ , errors :: [JSONError]
44+ }
45+
46+$(A.deriveJSON A.defaultOptions ''ErrorPosition)
47+$(A.deriveJSON A.defaultOptions ''JSONError)
48+$(A.deriveJSON A.defaultOptions ''JSONResult)
49+
50+toJSONErrors :: Bool -> P.Level -> P.MultipleErrors -> [JSONError]
51+toJSONErrors verbose level = map (toJSONError verbose level) . P.runMultipleErrors
52+
53+toJSONError :: Bool -> P.Level -> P.ErrorMessage -> JSONError
54+toJSONError verbose level e =
55+ JSONError (toErrorPosition <$> sspan)
56+ (P.renderBox (P.prettyPrintSingleError' verbose level (P.stripModuleAndSpan e)))
57+ (P.errorCode e)
58+ (P.spanName <$> sspan)
59+ (P.runModuleName <$> P.errorModule e)
60+ where
61+ sspan :: Maybe P.SourceSpan
62+ sspan = P.errorSpan e
63+
64+ toErrorPosition :: P.SourceSpan -> ErrorPosition
65+ toErrorPosition ss =
66+ ErrorPosition (P.sourcePosLine (P.spanStart ss))
67+ (P.sourcePosColumn (P.spanStart ss))
68+ (P.sourcePosLine (P.spanEnd ss))
69+ (P.sourcePosColumn (P.spanEnd ss))
psc/Main.hs+37−21
@@ -27,6 +27,9 @@ import Control.Monad.Writer.Strict
2727import Data.List (isSuffixOf, partition)
2828import Data.Version (showVersion)
2929import qualified Data.Map as M
30+import qualified Data.Aeson as A
31+import qualified Data.ByteString.Lazy as B
32+import qualified Data.ByteString.UTF8 as BU8
3033
3134import Options.Applicative as Opts
3235
@@ -40,45 +43,54 @@ import qualified Paths_purescript as Paths
4043
4144import Language.PureScript.Make
4245
46+import JSON
47+
4348data PSCMakeOptions = PSCMakeOptions
4449 { pscmInput :: [FilePath]
4550 , pscmForeignInput :: [FilePath]
4651 , pscmOutputDir :: FilePath
4752 , pscmOpts :: P.Options
4853 , pscmUsePrefix :: Bool
54+ , pscmJSONErrors :: Bool
4955 }
5056
5157data InputOptions = InputOptions
5258 { ioInputFiles :: [FilePath]
5359 }
5460
61+-- | Argumnets: verbose, use JSON, warnings, errors
62+printWarningsAndErrors :: Bool -> Bool -> P.MultipleErrors -> Either P.MultipleErrors a -> IO ()
63+printWarningsAndErrors verbose False warnings errors = do
64+ when (P.nonEmpty warnings) $
65+ hPutStrLn stderr (P.prettyPrintMultipleWarnings verbose warnings)
66+ case errors of
67+ Left errs -> do
68+ hPutStrLn stderr (P.prettyPrintMultipleErrors verbose errs)
69+ exitFailure
70+ Right _ -> return ()
71+printWarningsAndErrors verbose True warnings errors = do
72+ hPutStrLn stderr . BU8.toString . B.toStrict . A.encode $
73+ JSONResult (toJSONErrors verbose P.Warning warnings)
74+ (either (toJSONErrors verbose P.Error) (const []) errors)
75+ either (const exitFailure) (const (return ())) errors
76+
5577compile :: PSCMakeOptions -> IO ()
56−compile (PSCMakeOptions inputGlob inputForeignGlob outputDir opts usePrefix) = do
57− input <- globWarningOnMisses warnFileTypeNotFound inputGlob
78+compile PSCMakeOptions{..} = do
79+ input <- globWarningOnMisses warnFileTypeNotFound pscmInput
5880 when (null input) $ do
5981 hPutStrLn stderr "psc: No input files."
6082 exitFailure
6183 let (jsFiles, pursFiles) = partition (isSuffixOf ".js") input
6284 moduleFiles <- readInput (InputOptions pursFiles)
63− inputForeign <- globWarningOnMisses warnFileTypeNotFound inputForeignGlob
85+ inputForeign <- globWarningOnMisses warnFileTypeNotFound pscmForeignInput
6486 foreignFiles <- forM (inputForeign ++ jsFiles) (\inFile -> (inFile,) <$> readUTF8File inFile)
65− case runWriterT (parseInputs moduleFiles foreignFiles) of
66− Left errs -> do
67− hPutStrLn stderr (P.prettyPrintMultipleErrors (P.optionsVerboseErrors opts) errs)
68− exitFailure
69− Right ((ms, foreigns), warnings) -> do
70− when (P.nonEmpty warnings) $
71− hPutStrLn stderr (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings)
72− let filePathMap = M.fromList $ map (\(fp, P.Module _ _ mn _ _) -> (mn, fp)) ms
73− makeActions = buildMakeActions outputDir filePathMap foreigns usePrefix
74− (e, warnings') <- runMake opts $ P.make makeActions (map snd ms)
75− when (P.nonEmpty warnings') $
76− hPutStrLn stderr (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings')
77− case e of
78− Left errs -> do
79− hPutStrLn stderr (P.prettyPrintMultipleErrors (P.optionsVerboseErrors opts) errs)
80− exitFailure
81− Right _ -> exitSuccess
87+ (makeErrors, makeWarnings) <- runMake pscmOpts $ do
88+ (ms, foreigns) <- parseInputs moduleFiles foreignFiles
89+ let filePathMap = M.fromList $ map (\(fp, P.Module _ _ mn _ _) -> (mn, fp)) ms
90+ makeActions = buildMakeActions pscmOutputDir filePathMap foreigns pscmUsePrefix
91+ P.make makeActions (map snd ms)
92+ printWarningsAndErrors (P.optionsVerboseErrors pscmOpts) pscmJSONErrors makeWarnings makeErrors
93+ exitSuccess
8294
8395warnFileTypeNotFound :: String -> IO ()
8496warnFileTypeNotFound = hPutStrLn stderr . ("psc: No files found using pattern: " ++)
@@ -161,6 +173,10 @@ noPrefix = switch $
161173 <> long "no-prefix"
162174 <> help "Do not include comment header"
163175
176+jsonErrors :: Parser Bool
177+jsonErrors = switch $
178+ long "json-errors"
179+ <> help "Print errors to stderr as JSON"
164180
165181options :: Parser P.Options
166182options = P.Options <$> noTco
@@ -177,7 +193,7 @@ pscMakeOptions = PSCMakeOptions <$> many inputFile
177193 <*> outputDirectory
178194 <*> options
179195 <*> (not <$> noPrefix)
180−
196+ <*> jsonErrors
181197
182198main :: IO ()
183199main = execParser opts >>= compile
purescript.cabal+5−2
@@ -185,12 +185,15 @@ library
185185 ghc-options: -Wall -O2
186186
187187executable psc
188− build-depends: base >=4 && <5, containers -any, directory -any, filepath -any,
188+ build-depends: base >=4 && <5, base-compat >=0.6.0,
189+ containers -any, directory -any, filepath -any,
189190 mtl -any, optparse-applicative >= 0.10.0, parsec -any, purescript -any,
190− time -any, transformers -any, transformers-compat -any, Glob >= 0.7 && < 0.8
191+ time -any, transformers -any, transformers-compat -any, Glob >= 0.7 && < 0.8,
192+ aeson >= 0.8 && < 0.11, bytestring -any, utf8-string >= 1 && < 2
191193 main-is: Main.hs
192194 buildable: True
193195 hs-source-dirs: psc
196+ other-modules: JSON
194197 ghc-options: -Wall -O2 -fno-warn-unused-do-bind -threaded -rtsopts "-with-rtsopts=-N"
195198
196199executable psci
src/Language/PureScript/Errors.hs+28−2
@@ -184,6 +184,31 @@ data HintCategory
184184
185185data ErrorMessage = ErrorMessage [ErrorMessageHint] SimpleErrorMessage deriving (Show)
186186
187+-- | Get the source span for an error
188+errorSpan :: ErrorMessage -> Maybe SourceSpan
189+errorSpan = findHint matchSpan
190+ where
191+ matchSpan (PositionedError ss) = Just ss
192+ matchSpan _ = Nothing
193+
194+-- | Get the module name for an error
195+errorModule :: ErrorMessage -> Maybe ModuleName
196+errorModule = findHint matchModule
197+ where
198+ matchModule (ErrorInModule mn) = Just mn
199+ matchModule _ = Nothing
200+
201+findHint :: (ErrorMessageHint -> Maybe a) -> ErrorMessage -> Maybe a
202+findHint f (ErrorMessage hints _) = getLast . foldMap (Last . f) $ hints
203+
204+-- | Remove the module name and span hints from an error
205+stripModuleAndSpan :: ErrorMessage -> ErrorMessage
206+stripModuleAndSpan (ErrorMessage hints e) = ErrorMessage (filter (not . shouldStrip) hints) e
207+ where
208+ shouldStrip (ErrorInModule _) = True
209+ shouldStrip (PositionedError _) = True
210+ shouldStrip _ = False
211+
187212-- |
188213-- Get the error code for a particular error type
189214--
@@ -287,7 +312,6 @@ errorCode em = case unwrapErrorMessage em of
287312 DuplicateImport{} -> "DuplicateImport"
288313 IntOutOfRange{} -> "IntOutOfRange"
289314
290−
291315-- |
292316-- A stack trace for an error
293317--
@@ -304,7 +328,6 @@ nonEmpty = not . null . runMultipleErrors
304328errorMessage :: SimpleErrorMessage -> MultipleErrors
305329errorMessage err = MultipleErrors [ErrorMessage [] err]
306330
307−
308331-- |
309332-- Create an error set from a single error message
310333--
@@ -389,6 +412,9 @@ onTypesInErrorMessageM f (ErrorMessage hints simple) = ErrorMessage <$> traverse
389412 gHint (ErrorInInstance cl ts) = ErrorInInstance cl <$> traverse f ts
390413 gHint other = pure other
391414
415+prettyPrintSingleError' :: Bool -> Level -> ErrorMessage ->Box.Box
416+prettyPrintSingleError' full level = flip evalState defaultUnknownMap . prettyPrintSingleError full level
417+
392418-- |
393419-- Pretty print a single error, simplifying if necessary
394420--