axel-0.0.12: src/Axel/Haskell/Error.hs
{-# LANGUAGE TemplateHaskell #-}
module Axel.Haskell.Error where
import Axel.Prelude
import Axel.Sourcemap
( ModuleInfo
, Position(Position)
, SourcePosition
, renderSourcePosition
)
import qualified Axel.Sourcemap as SM
import Axel.Utils.FilePath (replaceExtension)
import Axel.Utils.Json (_Int)
import Axel.Utils.Text (encodeUtf8Lazy, indent)
import Control.Lens.Operators ((^.), (^?))
import Control.Lens.TH (makeFieldsNoPrefix)
import Control.Lens.Tuple (_1, _2)
import Control.Monad (join)
import qualified Data.Aeson as Json
import Data.Aeson.Lens (_String, key)
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
data GhcError =
GhcError
{ _message :: Text
, _sourcePosition :: SourcePosition
}
deriving (Show)
makeFieldsNoPrefix ''GhcError
processStackOutputLine :: ModuleInfo -> Text -> [Text]
processStackOutputLine moduleInfo line =
fromMaybe [line] (tryProcessGhcOutput moduleInfo line)
tryProcessGhcOutput :: ModuleInfo -> Text -> Maybe [Text]
tryProcessGhcOutput moduleInfo line = do
let obj `viewText` field = obj ^? field . _String
jsonLine <- Json.decode' @Json.Value (encodeUtf8Lazy line)
msg <- jsonLine `viewText` key "doc"
pure $ fromMaybe [msg] $ do
jsonSpan <- jsonLine ^? key "span"
startPosition <-
Position <$> (jsonSpan ^? key "startLine" . _Int) <*>
(jsonSpan ^? key "startCol" . _Int)
filePath <- jsonSpan `viewText` key "file"
let haskellSourcePosition = (T.unpack filePath, startPosition)
let maybeAxelError =
toAxelError moduleInfo $ GhcError msg haskellSourcePosition
let haskellError =
"\n" <> indent 4 msg <>
"The above message is in terms of the generated Haskell, at " <>
renderSourcePosition haskellSourcePosition <>
".\nIt couldn't be mapped to an Axel location (did a macro lose a sourcemapping annotation along the way?).\n"
pure [fromMaybe haskellError maybeAxelError]
toAxelError :: ModuleInfo -> GhcError -> Maybe Text
toAxelError moduleInfo ghcError = do
let haskellPath = ghcError ^. sourcePosition . _1
let haskellPosition = ghcError ^. sourcePosition . _2
let axelPath = replaceExtension (FilePath $ T.pack haskellPath) "axel"
let positionHint startPos = "at " <> renderSourcePosition startPos
SM.Output transpiledOutput <- M.lookup axelPath moduleInfo >>= snd
axelSourcePosition <-
join $ SM.findOriginalPosition transpiledOutput haskellPosition
pure $ "\n" <> indent 4 (ghcError ^. message) <>
"The above message is in terms of the generated Haskell, " <>
positionHint (haskellPath, haskellPosition) <>
".\nTry checking " <>
positionHint axelSourcePosition <>
".\nIf the Axel code at that position doesn't seem related, something may have gone wrong during a macro expansion.\n"