{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Rattus.Plugin.Utils (
printMessage,
Severity(..),
isRattModule,
isGhcModule,
getNameModule,
isStable,
isStrict,
isTemporal,
userFunction,
isType)
where
import ErrUtils
import Prelude hiding ((<>))
import GhcPlugins
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Char
import Data.Maybe
import MonadUtils
isType Type {} = True
isType (App e _) = isType e
isType (Cast e _) = isType e
isType (Tick _ e) = isType e
isType _ = False
printMessage :: (HasDynFlags m, MonadIO m) =>
Severity -> SrcSpan -> MsgDoc -> m ()
printMessage sev loc doc = do
dflags <- getDynFlags
let sty = case sev of
SevError -> defaultErrStyle dflags
SevWarning -> defaultErrStyle dflags
SevDump -> defaultDumpStyle dflags
_ -> defaultUserStyle dflags
liftIO $ putLogMsg dflags NoReason sev loc sty doc
rattModules :: Set FastString
rattModules = Set.fromList ["Rattus.Internal","Rattus.Primitives"
,"Rattus.Stable", "Rattus.Arrow"]
isRattModule :: FastString -> Bool
isRattModule = (`Set.member` rattModules)
isGhcModule :: FastString -> Bool
isGhcModule = (== "GHC.Types")
getNameModule :: NamedThing a => a -> Maybe (FastString, FastString)
getNameModule v = do
let name = getName v
mod <- nameModule_maybe name
return (getOccFS name,moduleNameFS (moduleName mod))
ghcStableTypes :: Set FastString
ghcStableTypes = Set.fromList ["Int","Bool","Float","Double","Char", "IO"]
newtype TypeCmp = TC Type
instance Eq TypeCmp where
(TC t1) == (TC t2) = eqType t1 t2
instance Ord TypeCmp where
compare (TC t1) (TC t2) = nonDetCmpType t1 t2
isTemporal :: Type -> Bool
isTemporal t = isTemporalRec 0 Set.empty t
isTemporalRec :: Int -> Set TypeCmp -> Type -> Bool
isTemporalRec d _ _ | d == 100 = False
isTemporalRec _ pr t | Set.member (TC t) pr = False
isTemporalRec d pr t = do
let pr' = Set.insert (TC t) pr
case splitTyConApp_maybe t of
Nothing -> False
Just (con,args) ->
case getNameModule con of
Nothing -> False
Just (name,mod)
| isRattModule mod && (name == "Box" || name == "O") -> True
| isFunTyCon con -> or (map (isTemporalRec (d+1) pr') args)
| isAlgTyCon con ->
case algTyConRhs con of
DataTyCon {data_cons = cons} -> or (map check cons)
where check con = case dataConInstSig con args of
(_, _,tys) -> or (map (isTemporalRec (d+1) pr') tys)
_ -> or (map (isTemporalRec (d+1) pr') args)
_ -> False
isStable :: Set Var -> Type -> Bool
isStable c t = isStableRec c 0 Set.empty t
isStableRec :: Set Var -> Int -> Set TypeCmp -> Type -> Bool
isStableRec _ d _ _ | d == 100 = True
isStableRec _ _ pr t | Set.member (TC t) pr = True
isStableRec c d pr t = do
let pr' = Set.insert (TC t) pr
case splitTyConApp_maybe t of
Nothing -> case getTyVar_maybe t of
Just v ->
v `Set.member` c
Nothing -> False
Just (con,args) ->
case getNameModule con of
Nothing -> False
Just (name,mod)
| isRattModule mod && name == "Box" -> True
| isGhcModule mod -> name `Set.member` ghcStableTypes
| isAlgTyCon con ->
case algTyConRhs con of
DataTyCon {data_cons = cons, is_enum = enum}
| enum -> True
| and $ concatMap (map isSrcStrict'
. dataConSrcBangs) $ cons ->
and (map check cons)
| otherwise -> False
where check con = case dataConInstSig con args of
(_, _,tys) -> and (map (isStableRec c (d+1) pr') tys)
TupleTyCon {} -> null args
_ -> False
_ -> False
isStrict :: Type -> Bool
isStrict t = isStrictRec 0 Set.empty t
isStrictRec :: Int -> Set TypeCmp -> Type -> Bool
isStrictRec d _ _ | d == 100 = True
isStrictRec _ pr t | Set.member (TC t) pr = True
isStrictRec d pr t = do
let pr' = Set.insert (TC t) pr
let (_,t') = splitForAllTys t
let (c, tys) = repSplitAppTys t'
if isJust (getTyVar_maybe c) then and (map (isStrictRec (d+1) pr') tys)
else case splitTyConApp_maybe t' of
Nothing -> isJust (getTyVar_maybe t)
Just (con,args) ->
case getNameModule con of
Nothing -> False
Just (name,mod)
| isRattModule mod && (name == "Box" || name == "O") -> True
| isGhcModule mod -> name `Set.member` ghcStableTypes
| isFunTyCon con -> True
| isAlgTyCon con ->
case algTyConRhs con of
DataTyCon {data_cons = cons, is_enum = enum}
| enum -> True
| and $ (map (isSrcStrictOrDelay args)) $ cons ->
and (map check cons)
| otherwise -> False
where check con = case dataConInstSig con args of
(_, _,tys) -> and (map (isStrictRec (d+1) pr') tys)
TupleTyCon {} -> null args
_ -> False
| otherwise -> False
isSrcStrictOrDelay :: [Type] -> DataCon -> Bool
isSrcStrictOrDelay args con = and (zipWith check tys (dataConSrcBangs con))
where (_, _,tys) = dataConInstSig con args
check ty b = isSrcStrict' b || isDelay ty
isDelay ty = case splitTyConApp_maybe ty of
Just (con,_) ->
case getNameModule con of
Just (name,mod) | isRattModule mod && name == "O" -> True
_ -> False
_ -> False
isSrcStrict' (HsSrcBang _ _ SrcStrict) = True
isSrcStrict' _ = False
userFunction :: Var -> Bool
userFunction v =
case getOccString (getName v) of
(c : _)
| isUpper c || c == '$' || c == ':' -> False
| otherwise -> True
_ -> False