splint-1.0.0.0: src/lib/Splint.hs
module Splint ( plugin ) where
import qualified Bag as GHC
import qualified Data.IORef as IORef
import qualified ErrUtils as GHC
import qualified GhcPlugins as GHC
import qualified Language.Haskell.HLint as HLint
import qualified System.IO.Unsafe as Unsafe
plugin :: GHC.Plugin
plugin = GHC.defaultPlugin
{ GHC.parsedResultAction = action
, GHC.pluginRecompile = GHC.purePlugin
}
action
:: [GHC.CommandLineOption]
-> GHC.ModSummary
-> GHC.HsParsedModule
-> GHC.Hsc GHC.HsParsedModule
action commandLineOptions _modSummary hsParsedModule = do
dynFlags <- GHC.getDynFlags
GHC.liftIO $ do
(_parseFlags, classifies, hint) <- getSettings commandLineOptions
let
apiAnns = GHC.hpm_annotations hsParsedModule
hsModule = GHC.hpm_module hsParsedModule
moduleEx = HLint.createModuleEx apiAnns hsModule
ideas = HLint.applyHints classifies hint [moduleEx]
GHC.printOrThrowWarnings dynFlags
. GHC.listToBag
. fmap (ideaToWarnMsg dynFlags)
$ filter ((/= HLint.Ignore) . HLint.ideaSeverity) ideas
pure hsParsedModule
type Settings = (HLint.ParseFlags, [HLint.Classify], HLint.Hint)
getSettings :: [GHC.CommandLineOption] -> IO Settings
getSettings commandLineOptions = do
maybeSettings <- IORef.readIORef settingsRef
case maybeSettings of
Just settings -> pure settings
Nothing -> do
settings <- HLint.argsSettings commandLineOptions
IORef.writeIORef settingsRef $ Just settings
pure settings
{-# NOINLINE settingsRef #-}
settingsRef :: IORef.IORef (Maybe Settings)
settingsRef = Unsafe.unsafePerformIO $ IORef.newIORef Nothing
ideaToWarnMsg :: GHC.DynFlags -> HLint.Idea -> GHC.WarnMsg
ideaToWarnMsg dynFlags idea =
let
mkErrMsg = case HLint.ideaSeverity idea of
HLint.Error -> GHC.mkPlainErrMsg
_ -> GHC.mkPlainWarnMsg
srcSpan = HLint.ideaSpan idea
msgDoc = ideaToMsgDoc idea
in mkErrMsg dynFlags srcSpan msgDoc
ideaToMsgDoc :: HLint.Idea -> GHC.MsgDoc
ideaToMsgDoc idea = GHC.vcat
[ GHC.text $ HLint.ideaHint idea
, case HLint.ideaTo idea of
Just to | not $ null to -> GHC.text $ "Perhaps: " <> to
_ -> GHC.empty
, GHC.vcat . fmap (GHC.text . mappend "Note: " . show) $ HLint.ideaNote idea
]