{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} module Test.Tasty.AutoCollect.GHC ( module Test.Tasty.AutoCollect.GHC.Shim, -- * Builders genFuncSig, genFuncDecl, lhsvar, mkHsAppTypes, -- * Located utilities genLoc, firstLocatedWhere, getSpanLine, -- * Name utilities mkRdrName, mkLRdrName, mkRdrNameType, mkLRdrNameType, fromRdrName, thNameToGhcNameIO, ) where import Data.Foldable (foldl') import Data.IORef (IORef) import Data.List (sortOn) import Data.Maybe (fromMaybe, listToMaybe, mapMaybe) import qualified Language.Haskell.TH as TH import Test.Tasty.AutoCollect.GHC.Shim {----- Builders -----} genFuncSig :: LocatedN RdrName -> LHsType GhcPs -> HsDecl GhcPs genFuncSig funcName funcType = SigD noExtField . TypeSig noAnn [funcName] . hsTypeToHsSigWcType $ funcType -- | Make simple function declaration of the form ` = where ` genFuncDecl :: LocatedN RdrName -> [LPat GhcPs] -> LHsExpr GhcPs -> Maybe (HsLocalBinds GhcPs) -> HsDecl GhcPs genFuncDecl funcName funcArgs funcBody mFuncWhere = ValD NoExtField . mkFunBind Generated funcName $ [ mkMatch (mkPrefixFunRhs funcName) funcArgs funcBody funcWhere ] where funcWhere = fromMaybe emptyLocalBinds mFuncWhere lhsvar :: LocatedN RdrName -> LHsExpr GhcPs lhsvar = genLoc . HsVar NoExtField mkHsAppTypes :: LHsExpr GhcPs -> [LHsType GhcPs] -> LHsExpr GhcPs mkHsAppTypes = foldl' mkHsAppType mkHsAppType :: LHsExpr GhcPs -> LHsType GhcPs -> LHsExpr GhcPs mkHsAppType e t = genLoc $ HsAppType xAppTypeE e (HsWC noExtField t) {----- Located utilities -----} genLoc :: e -> GenLocated (SrcAnn ann) e genLoc = L generatedSrcAnn firstLocatedWhere :: Ord l => (GenLocated l e -> Maybe a) -> [GenLocated l e] -> Maybe a firstLocatedWhere f = listToMaybe . mapMaybe f . sortOn getLoc getSpanLine :: GenLocated (SrcSpanAnn' a) e -> String getSpanLine loc = case srcSpanStart $ getLocA loc of Right srcLoc -> "line " ++ show (srcLocLine srcLoc) Left s -> s {----- Name utilities -----} mkRdrName :: String -> RdrName mkRdrName = mkRdrUnqual . mkOccNameVar mkLRdrName :: String -> LocatedN RdrName mkLRdrName = genLoc . mkRdrName mkRdrNameType :: String -> RdrName mkRdrNameType = mkRdrUnqual . mkOccNameTC mkLRdrNameType :: String -> LocatedN RdrName mkLRdrNameType = genLoc . mkRdrNameType fromRdrName :: LocatedN RdrName -> String fromRdrName = occNameString . rdrNameOcc . unLoc -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8492 thNameToGhcNameIO :: HscEnv -> IORef NameCache -> TH.Name -> IO (Maybe Name) thNameToGhcNameIO hscEnv cache name = fmap fst . runCoreM hscEnv{hsc_NC = cache} (unused "cr_rule_base") (strict '.') (unused "cr_module") (strict mempty) (unused "cr_print_unqual") (unused "cr_loc") $ thNameToGhcName name where unused msg = error $ "unexpectedly used: " ++ msg -- marks fields that are strict, so we can't use `unused` strict = id