packages feed

hoogle-5.0.19.0: src/Input/ParseDecl.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PatternSynonyms #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -Wno-missing-signatures -Wno-missing-pattern-synonym-signatures #-}

module Input.ParseDecl (myParseDecl) where

import Prelude hiding (Foldable(..))
import Data.Char (isAlphaNum, isUpper)
import Data.Foldable (Foldable(..))
import Data.List.Extra (dropEnd1, drop1, enumerate, stripPrefix, unsnoc, isInfixOf)
import Data.List.NonEmpty qualified as NE (toList)
import Data.Maybe (isNothing)
import GHC.Data.EnumSet qualified as EnumSet
import GHC.Data.FastString (unpackFS)
import GHC.Data.StringBuffer (stringToStringBuffer)
import GHC.Hs
import GHC.LanguageExtensions.Type (Extension (..))
import GHC.Parser (parseDeclaration)
import GHC.Parser.Lexer (P (..), ParseResult (..), initParserState, mkParserOpts)
import GHC.Types.Fixity (Fixity (..), FixityDirection (..))
import GHC.Types.Name (nameOccName, tvName)
import GHC.Types.Name.Occurrence (OccName (..), occNameString)
import GHC.Types.Name.Reader (RdrName (..), rdrNameOcc, rdrNameSpace)
import GHC.Types.SourceText (SourceText (..))
import GHC.Types.SrcLoc (GenLocated (..), mkRealSrcLoc, unLoc)
import GHC.Unit (GenModule (..))
import GHC.Utils.Error
import GHC.Utils.Outputable (Outputable (..), defaultSDocContext, runSDoc)
import Language.Haskell.Exts qualified as HSE
import Text.Read (readMaybe)

#if !MIN_VERSION_ghc_lib_parser(9,12,0)
pattern HsBang x y <- HsSrcBang _ x y
#endif

#if MIN_VERSION_ghc_lib_parser(9,12,0)
pattern MyFixity x y <- Fixity x y
#else
pattern MyFixity x y <- Fixity _ x y
#endif

#if MIN_VERSION_ghc_lib_parser(9,10,0)
pattern MyPrefixConGADT x <- PrefixConGADT _ x
pattern MyRecConGADT x <- RecConGADT _ x
#else
pattern MyPrefixConGADT x <- PrefixConGADT x
pattern MyRecConGADT x <- RecConGADT x _
#endif

#if !MIN_VERSION_ghc_lib_parser(9,14,0)
#define con_outer_bndrs con_bndrs
#endif

myParseDecl :: String -> HSE.ParseResult (HSE.Decl ())
myParseDecl str = case runGhcLibParser str of
    POk _state x -> case hsDeclToDecl (unLoc x) of
        Nothing -> HSE.ParseFailed HSE.noLoc str
        Just res -> HSE.ParseOk res
    PFailed _state -> HSE.ParseFailed HSE.noLoc str

hsDeclToDecl :: HsDecl GhcPs -> Maybe (HSE.Decl ())
hsDeclToDecl (TyClD _ (SynDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdRhs})) =
    Just $
        HSE.TypeDecl
            ()
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
                hsq_explicit
            )
            (hsTypeToType $ unLoc tcdRhs)
hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [], dd_ctxt, dd_kindSig = Nothing}})) =
    Just $
        HSE.DataDecl
            ()
            (HSE.DataType ())
            (fmap (hsTypesToContext . unLoc) dd_ctxt)
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
                hsq_explicit
            )
            []
            []
hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [], dd_kindSig = Just kind}})) =
    Just $
        HSE.GDataDecl
            ()
            (HSE.DataType ())
            Nothing
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
                hsq_explicit
            )
            (Just $ hsTypeToType $ unLoc kind)
            []
            []
hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [L _ (ConDeclGADT{con_names, con_outer_bndrs, con_g_args = MyPrefixConGADT args, con_res_ty, con_mb_cxt})]}})) =
    Just $
        HSE.GDataDecl
            ()
            (HSE.DataType ())
            Nothing
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
                hsq_explicit
            )
            Nothing
            ( map
                ( \con_name ->
                    HSE.GadtDecl
                        ()
                        (rdrNameToName $ unLoc con_name)
                        Nothing
                        Nothing
                        Nothing
                        ( maybe id (\bs -> applyTyForall (Just bs) Nothing) (hsOuterTyVarBndrsToFoo $ unLoc con_outer_bndrs) $
                            maybe id (applyTyForall Nothing . Just . hsTypesToContext . unLoc) con_mb_cxt $
                                foldr
#if MIN_VERSION_ghc_lib_parser(9,14,0)
                                    (\CDF{cdf_type} -> HSE.TyFun () (hsTypeToType $ unLoc cdf_type))
#else
                                    (\(HsScaled _ a) -> HSE.TyFun () (hsTypeToType $ unLoc a))
#endif
                                    (hsTypeToType $ unLoc con_res_ty)
                                    args
                        )
                )
                (NE.toList con_names)
            )
            []
hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [L _ (ConDeclGADT{con_names, con_outer_bndrs, con_g_args = MyRecConGADT (L _ args), con_res_ty, con_mb_cxt})]}})) =
    Just $
        HSE.GDataDecl
            ()
            (HSE.DataType ())
            Nothing
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
                hsq_explicit
            )
            Nothing
            ( map
                ( \con_name ->
                    HSE.GadtDecl
                        ()
                        (rdrNameToName $ unLoc con_name)
                        Nothing
                        Nothing
                        (Just $ map (conDeclFieldToFieldDecl . unLoc) args)
                        ( maybe id (HSE.TyForall () Nothing . Just . hsTypesToContext . unLoc) con_mb_cxt $
                            maybe id (\bs -> HSE.TyForall () (Just bs) Nothing) (hsOuterTyVarBndrsToFoo $ unLoc con_outer_bndrs) $
                                hsTypeToType $
                                    unLoc con_res_ty
                        )
                )
                (NE.toList con_names)
            )
            []
hsDeclToDecl (TyClD _ (FamDecl{tcdFam = FamilyDecl{fdLName, fdInfo = DataFamily, fdTyVars = HsQTvs{hsq_explicit}, fdResultSig}})) =
    Just $
        HSE.DataFamDecl
            ()
            Nothing
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc fdLName)
                hsq_explicit
            )
            (familyResultSigToResultSig $ unLoc fdResultSig)
hsDeclToDecl (TyClD _ (FamDecl{tcdFam = FamilyDecl{fdLName, fdTyVars = HsQTvs{hsq_explicit}, fdResultSig, fdInjectivityAnn}})) =
    Just $
        HSE.TypeFamDecl
            ()
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc fdLName)
                hsq_explicit
            )
            (familyResultSigToResultSig $ unLoc fdResultSig)
            (fmap (injectivityAnnToInjectivityInfo . unLoc) fdInjectivityAnn)
hsDeclToDecl (TyClD _ (GHC.Hs.ClassDecl{tcdCtxt, tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdFDs})) =
    Just $
        HSE.ClassDecl
            ()
            (fmap (hsTypesToContext . unLoc) tcdCtxt)
            ( foldl'
                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
                hsq_explicit
            )
            (map (funDepToFunDep . unLoc) tcdFDs)
            Nothing
hsDeclToDecl (SigD _ (GHC.Hs.TypeSig _ names (HsWC{hswc_body = L _ HsSig{sig_body}}))) =
    Just $
        HSE.TypeSig
            ()
            (map (rdrNameToName . unLoc) names)
            (hsTypeToType $ unLoc sig_body)
hsDeclToDecl (SigD _ (GHC.Hs.PatSynSig _ names (L _ HsSig{sig_body}))) =
    Just $ case hsTypeToType (unLoc sig_body) of
        HSE.TyForall () Nothing (Just ctx1) (HSE.TyForall () Nothing (Just ctx2) ty) ->
            HSE.PatSynSig
                ()
                (map (rdrNameToName . unLoc) names)
                Nothing
                (Just ctx1)
                Nothing
                (Just ctx2)
                ty
        HSE.TyForall () Nothing (Just ctx) ty ->
            HSE.PatSynSig
                ()
                (map (rdrNameToName . unLoc) names)
                Nothing
                (Just ctx)
                Nothing
                Nothing
                ty
        ty ->
            HSE.PatSynSig
                ()
                (map (rdrNameToName . unLoc) names)
                Nothing
                Nothing
                Nothing
                Nothing
                ty
hsDeclToDecl (SigD _ (FixSig _ (FixitySig _ names (MyFixity priority direction)))) =
    Just $
        HSE.InfixDecl
            ()
            (fixityDirectionToAssoc direction)
            (Just priority)
            (map (varOpOrConOp . rdrNameToName . unLoc) names)
hsDeclToDecl (InstD _ (ClsInstD{cid_inst = ClsInstDecl{cid_poly_ty = (L _ HsSig{sig_bndrs, sig_body})}})) =
    Just $ case hsTypeToType (unLoc sig_body) of
        HSE.TyForall () Nothing ctxt body ->
            HSE.InstDecl
                ()
                Nothing
                (HSE.IRule () (hsOuterTyVarBndrsToFoo sig_bndrs) ctxt (typeToInstHead body))
                Nothing
        body ->
            HSE.InstDecl
                ()
                Nothing
                (HSE.IRule () (hsOuterTyVarBndrsToFoo sig_bndrs) Nothing (typeToInstHead body))
                Nothing
hsDeclToDecl _ = Nothing

injectivityAnnToInjectivityInfo :: InjectivityAnn GhcPs -> HSE.InjectivityInfo ()
injectivityAnnToInjectivityInfo = \case
    InjectivityAnn _ lhs rhs ->
        HSE.InjectivityInfo () (rdrNameToName $ unLoc lhs) (map (rdrNameToName . unLoc) rhs)

#if MIN_VERSION_ghc_lib_parser(9,14,0)
conDeclFieldToFieldDecl :: HsConDeclRecField GhcPs -> HSE.FieldDecl ()
conDeclFieldToFieldDecl = \case
    HsConDeclRecField{cdrf_names, cdrf_spec = CDF{cdf_type}} ->
        HSE.FieldDecl
            ()
            (map (fieldOccToName . unLoc) cdrf_names)
            (hsTypeToType $ unLoc cdf_type)
#else
conDeclFieldToFieldDecl :: ConDeclField GhcPs -> HSE.FieldDecl ()
conDeclFieldToFieldDecl = \case
    ConDeclField{cd_fld_names, cd_fld_type} ->
        HSE.FieldDecl
            ()
            (map (fieldOccToName . unLoc) cd_fld_names)
            (hsTypeToType $ unLoc cd_fld_type)
#endif

fieldOccToName :: FieldOcc GhcPs -> HSE.Name ()
fieldOccToName = \case
    FieldOcc{foLabel} -> rdrNameToName $ unLoc foLabel

varOpOrConOp :: HSE.Name () -> HSE.Op ()
varOpOrConOp name = case name of
    HSE.Symbol () (':' : _) -> HSE.ConOp () name
    HSE.Ident () (c : _)
        | isUpper c -> HSE.ConOp () name
        | otherwise -> HSE.VarOp () name
    _ -> HSE.VarOp () name

familyResultSigToResultSig :: FamilyResultSig GhcPs -> Maybe (HSE.ResultSig ())
familyResultSigToResultSig = \case
    NoSig{} -> Nothing
    GHC.Hs.KindSig _ kind ->
        Just $ HSE.KindSig () $ hsTypeToType $ unLoc kind
    GHC.Hs.TyVarSig _ tvb ->
        Just $ HSE.TyVarSig () $ hsTyVarBndrToTyVarBind $ unLoc tvb

hsOuterTyVarBndrsToFoo :: HsOuterSigTyVarBndrs GhcPs -> Maybe [HSE.TyVarBind ()]
hsOuterTyVarBndrsToFoo = \case
    HsOuterImplicit{} -> Nothing
    HsOuterExplicit{hso_bndrs} -> Just $ map (hsTyVarBndrToTyVarBind . unLoc) hso_bndrs

funDepToFunDep :: GHC.Hs.FunDep GhcPs -> HSE.FunDep ()
funDepToFunDep = \case
    GHC.Hs.FunDep _ lhs rhs ->
        HSE.FunDep () (map (rdrNameToName . unLoc) lhs) (map (rdrNameToName . unLoc) rhs)

fixityDirectionToAssoc :: FixityDirection -> HSE.Assoc ()
fixityDirectionToAssoc = \case
    InfixL -> HSE.AssocLeft ()
    InfixR -> HSE.AssocRight ()
    InfixN -> HSE.AssocNone ()

hsTyVarBndrToTyVarBind ::
    HsTyVarBndr a GhcPs ->
    HSE.TyVarBind ()
#if MIN_VERSION_ghc_lib_parser(9,12,0)
hsTyVarBndrToTyVarBind = \case
    HsTvb _ _ (HsBndrVar _ (L _ var)) HsBndrNoKind{} ->
        HSE.UnkindedVar () (rdrNameToName var)
    HsTvb _ _ (HsBndrVar _ (L _ var)) (HsBndrKind _ (L _ kind)) ->
        HSE.KindedVar () (rdrNameToName var) (hsTypeToType kind)
    HsTvb _ _ (HsBndrWildCard _) _ ->
        HSE.UnkindedVar () (HSE.Ident () "_")
#else
hsTyVarBndrToTyVarBind = \case
    UserTyVar _ _ (L _ var) ->
        HSE.UnkindedVar () (rdrNameToName var)
    KindedTyVar _ _ (L _ var) (L _ kind) ->
        HSE.KindedVar () (rdrNameToName var) (hsTypeToType kind)
#endif

occNameToName :: OccName -> HSE.Name ()
occNameToName occ = case occNameString occ of
    xs@(x : _)
        | not (isAlphaNum x) && x /= '(' && x /= '_' -> HSE.Symbol () xs
    xs -> HSE.Ident () xs

rdrNameToName :: RdrName -> HSE.Name ()
rdrNameToName = occNameToName . rdrNameOcc

rdrNameToQName :: RdrName -> HSE.QName ()
rdrNameToQName = \case
    Unqual occName ->
        HSE.UnQual () $ occNameToName occName
    GHC.Types.Name.Reader.Qual modName occName ->
        HSE.Qual
            ()
            (HSE.ModuleName () (moduleNameString modName))
            (occNameToName occName)
    Orig modul occName ->
        HSE.Qual
            ()
            (HSE.ModuleName () (moduleNameString $ moduleName modul))
            (occNameToName occName)
    Exact name ->
        HSE.UnQual () $ occNameToName (nameOccName name)

hsTypeToType :: HsType GhcPs -> HSE.Type ()
hsTypeToType = \case
    HsListTy _ x ->
        HSE.TyList () $ hsTypeToType $ unLoc x
    HsTyVar _ _ (L _ (Exact x))
        | show (runSDoc (ppr x) defaultSDocContext) == "[]" ->
            HSE.TyCon () $ HSE.Special () $ HSE.ListCon ()
        | show (runSDoc (ppr x) defaultSDocContext) == "->" ->
            HSE.TyCon () $ HSE.Special () $ HSE.FunCon ()
        | Just n <- stripPrefix "Tuple" (occNameString (nameOccName x))
        , Just n' <- readMaybe n ->
            HSE.TyCon () $ HSE.Special () $ HSE.TupleCon () HSE.Boxed n'
    HsTyVar _ IsPromoted (L _ x) ->
        HSE.TyPromoted () $ HSE.PromotedCon () True $ rdrNameToQName x
    HsTyVar _ NotPromoted (L _ x) ->
        case rdrNameSpace x of
            ns
                | ns == tvName ->
                    HSE.TyVar () $ rdrNameToName x
            _ -> HSE.TyCon () $ rdrNameToQName x
    HsAppTy _ x y ->
        HSE.TyApp () (hsTypeToType $ unLoc x) (hsTypeToType $ unLoc y)
    HsFunTy _ _ x y ->
        HSE.TyFun () (hsTypeToType $ unLoc x) (hsTypeToType $ unLoc y)
    HsTupleTy _ HsBoxedOrConstraintTuple [] ->
        HSE.TyCon () $ HSE.Special () $ HSE.UnitCon ()
    HsTupleTy _ HsUnboxedTuple [] ->
        HSE.TyCon () $ HSE.Special () $ HSE.UnboxedSingleCon ()
    HsTupleTy _ boxed xs ->
        HSE.TyTuple () (hsTupleSortToBoxed boxed) (map (hsTypeToType . unLoc) xs)
    HsStarTy _ _ ->
        HSE.TyStar ()
#if !MIN_VERSION_ghc_lib_parser(9,14,0)
    HsBangTy _ (HsBang unpackedness strictness) x ->
        applyTyBang
            (srcStrictnessToBangType strictness)
            (srcUnpackednessToUnpackedness unpackedness)
            (hsTypeToType $ unLoc x)
#endif
    HsParTy _ x -> case hsTypeToType (unLoc x) of
        x'@HSE.TyKind{} -> x'
        x' -> HSE.TyParen () x'
    HsQualTy{hst_ctxt, hst_body} ->
        applyTyForall Nothing (Just $ hsTypesToContext $ unLoc hst_ctxt) $
            hsTypeToType $
                unLoc hst_body
    HsForAllTy{hst_tele = HsForAllInvis{hsf_invis_bndrs}, hst_body} ->
        applyTyForall
            (Just $ map (hsTyVarBndrToTyVarBind . unLoc) hsf_invis_bndrs)
            Nothing
            (hsTypeToType $ unLoc hst_body)
    -- TODO FIXME when migrating to ghc-lib-parser completely:
    -- HSE does not support forall with visible binders
    HsForAllTy{hst_tele = HsForAllVis{hsf_vis_bndrs}, hst_body} ->
        applyTyForall
            (Just $ map (hsTyVarBndrToTyVarBind . unLoc) hsf_vis_bndrs)
            Nothing
            (hsTypeToType $ unLoc hst_body)
    HsExplicitListTy _ IsPromoted xs ->
        HSE.TyPromoted () $ HSE.PromotedList () True (map (hsTypeToType . unLoc) xs)
    HsExplicitListTy _ NotPromoted [x] ->
        HSE.TyList () $ hsTypeToType $ unLoc x
    HsExplicitListTy _ NotPromoted xs ->
        HSE.TyPromoted () $ HSE.PromotedList () False (map (hsTypeToType . unLoc) xs)

#if MIN_VERSION_ghc_lib_parser(9,12,0)
    HsExplicitTupleTy _ IsPromoted [] ->
        HSE.TyPromoted () $ HSE.PromotedCon () True $ HSE.Special () $ HSE.UnitCon ()
    HsExplicitTupleTy _ IsPromoted xs ->
        HSE.TyPromoted () $ HSE.PromotedTuple () $ map (hsTypeToType . unLoc) xs
    HsExplicitTupleTy _ NotPromoted xs ->
        HSE.TyTuple () HSE.Boxed (map (hsTypeToType . unLoc) xs)
#else
    HsExplicitTupleTy _ [] ->
        HSE.TyPromoted () $ HSE.PromotedCon () True $ HSE.Special () $ HSE.UnitCon ()
    HsExplicitTupleTy _ xs ->
        HSE.TyPromoted () $ HSE.PromotedTuple () $ map (hsTypeToType . unLoc) xs
#endif

    HsOpTy _ _ x (L _ (Unqual y)) z
        | occNameString y == "~" ->
            HSE.TyEquals () (hsTypeToType $ unLoc x) (hsTypeToType $ unLoc z)
    HsOpTy _ promotion x (L _ (Exact y)) z
        | occNameString (nameOccName y) == ":" ->
            HSE.TyInfix
                ()
                (hsTypeToType $ unLoc x)
                (promotionFlagToMaybePromotedName promotion $ HSE.Special () $ HSE.Cons ())
                (hsTypeToType $ unLoc z)
    HsOpTy _ promotion x y z ->
        HSE.TyInfix
            ()
            (hsTypeToType $ unLoc x)
            (promotionFlagToMaybePromotedName promotion $ rdrNameToQName $ unLoc y)
            (hsTypeToType $ unLoc z)
    HsKindSig _ lhs rhs ->
        HSE.TyKind () (hsTypeToType $ unLoc lhs) (hsTypeToType $ unLoc rhs)
    HsTyLit _ (HsNumTy (SourceText txt) val) ->
        HSE.TyPromoted () $ HSE.PromotedInteger () val (unpackFSOrId txt)
    HsTyLit _ (HsStrTy (SourceText txt) val) ->
        HSE.TyPromoted () $ HSE.PromotedString () (unpackFS val) (drop1 $ dropEnd1 $ unpackFSOrId txt)
    HsSumTy _ xs ->
        HSE.TyUnboxedSum () $ map (hsTypeToType . unLoc) xs
    HsWildCardTy _ ->
        HSE.TyWildCard () Nothing
    -- Everything else cannot be represented in HSE, so replacing with a wildcard
    _ -> HSE.TyWildCard () Nothing

promotionFlagToMaybePromotedName ::
    PromotionFlag -> HSE.QName () -> HSE.MaybePromotedName ()
promotionFlagToMaybePromotedName = \case
    NotPromoted -> HSE.UnpromotedName ()
    IsPromoted -> HSE.PromotedName ()

applyTyForall ::
    Maybe [HSE.TyVarBind ()] -> Maybe (HSE.Context ()) -> HSE.Type () -> HSE.Type ()
applyTyForall mArg1 mArg2 = \case
    HSE.TyForall () Nothing mArg2' ty
        | isNothing mArg2 -> HSE.TyForall () mArg1 mArg2' ty
    ty -> HSE.TyForall () mArg1 mArg2 ty

#if !MIN_VERSION_ghc_lib_parser(9,14,0)
applyTyBang ::
    HSE.BangType () -> HSE.Unpackedness () -> HSE.Type () -> HSE.Type ()
applyTyBang bang unpack = \case
    HSE.TyBang () (HSE.NoStrictAnnot ()) unpack' ty
        | unpack == HSE.NoUnpackPragma () -> HSE.TyBang () bang unpack' ty
    HSE.TyBang () bang' (HSE.NoUnpackPragma ()) ty
        | bang == HSE.NoStrictAnnot () -> HSE.TyBang () bang' unpack ty
    HSE.TyApp () x y -> HSE.TyApp () (applyTyBang bang unpack x) y
    ty -> HSE.TyBang () bang unpack ty

srcStrictnessToBangType :: SrcStrictness -> HSE.BangType ()
srcStrictnessToBangType = \case
    SrcLazy -> HSE.LazyTy ()
    SrcStrict -> HSE.BangedTy ()
    NoSrcStrict -> HSE.NoStrictAnnot ()

srcUnpackednessToUnpackedness :: SrcUnpackedness -> HSE.Unpackedness ()
srcUnpackednessToUnpackedness = \case
    SrcUnpack -> HSE.Unpack ()
    SrcNoUnpack -> HSE.NoUnpack ()
    NoSrcUnpack -> HSE.NoUnpackPragma ()
#endif

typeToInstHead :: HSE.Type () -> HSE.InstHead ()
typeToInstHead = \case
    HSE.TyApp () x y -> HSE.IHApp () (typeToInstHead x) y
    HSE.TyCon () x -> HSE.IHCon () x
    HSE.TyInfix () x (HSE.UnpromotedName () y) z -> HSE.IHApp () (HSE.IHInfix () x y) z
    -- The rest happens only in ghc-prim, which are likely some magical forms.
    -- Let's skip them.
    _ -> HSE.IHCon () $ HSE.Special () $ HSE.UnitCon ()

hsTypesToContext ::
    [GenLocated SrcSpanAnnA (HsType GhcPs)] ->
    HSE.Context ()
hsTypesToContext = \case
    [] -> HSE.CxEmpty ()
    [x] -> HSE.CxSingle () $ hsTypeToAsst $ unLoc x
    xs -> HSE.CxTuple () $ map (hsTypeToAsst . unLoc) xs

hsTypeToAsst :: HsType GhcPs -> HSE.Asst ()
hsTypeToAsst = \case
    HsIParamTy _ name t ->
        HSE.IParam () (hsIPNameToIPName $ unLoc name) (hsTypeToType $ unLoc t)
    HsParTy _ t ->
        HSE.ParenA () $ hsTypeToAsst $ unLoc t
    t -> case hsTypeToType t of
        HSE.TyParen () ty -> HSE.ParenA () $ HSE.TypeA () ty
        ty -> HSE.TypeA () ty

hsIPNameToIPName :: HsIPName -> HSE.IPName ()
hsIPNameToIPName = HSE.IPDup () . unpackFS . hsIPNameFS

hsTupleSortToBoxed :: HsTupleSort -> HSE.Boxed
hsTupleSortToBoxed = \case
    HsUnboxedTuple -> HSE.Unboxed
    HsBoxedOrConstraintTuple -> HSE.Boxed

runGhcLibParser ::
    String ->
    GHC.Parser.Lexer.ParseResult (GenLocated SrcSpanAnnA (HsDecl GhcPs))
runGhcLibParser str
    | Just (str', ';') <- unsnoc str =
        runGhcLibParser str'
runGhcLibParser str = case runGhcLibParserWithExtensions almostAllExtensions str of
    PFailed{}
        | '#' `elem` str -> runGhcLibParserWithExtensions noUnboxed str
        | '*' `elem` str -> runGhcLibParserWithExtensions noStarIsType str
        | "pattern" `isInfixOf` str -> runGhcLibParserWithExtensions noPatternSynonyms str
    res -> res

allExtensions :: EnumSet.EnumSet Extension
allExtensions = EnumSet.fromList enumerate

almostAllExtensions :: EnumSet.EnumSet Extension
almostAllExtensions =
    foldr
        EnumSet.delete
        allExtensions
        [ Arrows -- makes "proc" a keyword
        , RecursiveDo -- makes "mdo" and "rec" keywords
        , StaticPointers -- makes "static" a keyword
        , TransformListComp -- makes "by", "group" and "using" keywords
        ]

noUnboxed :: EnumSet.EnumSet Extension
noUnboxed =
    foldr
        EnumSet.delete
        almostAllExtensions
        [ UnboxedSums
        , UnboxedTuples
        ]

noPatternSynonyms :: EnumSet.EnumSet Extension
noPatternSynonyms = EnumSet.delete PatternSynonyms almostAllExtensions

noStarIsType :: EnumSet.EnumSet Extension
noStarIsType = EnumSet.delete StarIsType almostAllExtensions

runGhcLibParserWithExtensions ::
    EnumSet.EnumSet Extension ->
    String ->
    GHC.Parser.Lexer.ParseResult (GenLocated SrcSpanAnnA (HsDecl GhcPs))
runGhcLibParserWithExtensions extensions str = unP parseDeclaration parseState
  where
#if MIN_VERSION_ghc_lib_parser(9,14,0)
    opts = mkParserOpts extensions emptyDiagOpts False False False False
#else
    opts = mkParserOpts extensions emptyDiagOpts [] False False False False
#endif
    dummyLocation = mkRealSrcLoc mempty 1 1
    buffer = stringToStringBuffer str
    parseState = initParserState opts buffer dummyLocation

#if !MIN_VERSION_ghc_lib_parser(9,8,0)
emptyDiagOpts :: DiagOpts
emptyDiagOpts = DiagOpts mempty mempty False False Nothing defaultSDocContext
#endif

#if MIN_VERSION_ghc_lib_parser(9,8,0)
unpackFSOrId = unpackFS
#else
unpackFSOrId = id
#endif