scrod-0.2026.2.21: source/library/Scrod/Convert/FromGhc/FamilyInstanceParents.hs
-- | Resolve family instance parent relationships.
--
-- Associates type family instance and data family instance items with
-- their corresponding family declarations when both are defined in the
-- same module.
module Scrod.Convert.FromGhc.FamilyInstanceParents where
import qualified Data.Map as Map
import GHC.Hs ()
import qualified GHC.Hs.Extension as Ghc
import qualified GHC.Parser.Annotation as Annotation
import qualified GHC.Types.SrcLoc as SrcLoc
import qualified Language.Haskell.Syntax as Syntax
import qualified Scrod.Convert.FromGhc.Internal as Internal
import qualified Scrod.Core.Item as Item
import qualified Scrod.Core.ItemKey as ItemKey
import qualified Scrod.Core.ItemKind as ItemKind
import qualified Scrod.Core.ItemName as ItemName
import qualified Scrod.Core.Located as Located
import qualified Scrod.Core.Location as Location
-- | Extract a map from source locations of family instance declarations
-- to the family name they reference.
extractFamilyInstanceNames ::
SrcLoc.Located (Syntax.HsModule Ghc.GhcPs) ->
Map.Map Location.Location ItemName.ItemName
extractFamilyInstanceNames lHsModule =
let hsModule = SrcLoc.unLoc lHsModule
decls = Syntax.hsmodDecls hsModule
in Map.fromList $ concatMap extractDeclFamilyInstanceName decls
-- | Extract family instance name from a single declaration.
extractDeclFamilyInstanceName ::
Syntax.LHsDecl Ghc.GhcPs ->
[(Location.Location, ItemName.ItemName)]
extractDeclFamilyInstanceName lDecl = case SrcLoc.unLoc lDecl of
Syntax.InstD _ (Syntax.TyFamInstD _ tyFamInst) ->
let eqn = Syntax.tfid_eqn tyFamInst
familyName = Internal.extractIdPName $ Syntax.feqn_tycon eqn
in foldMap (\loc -> [(loc, familyName)]) $
Internal.locationFromSrcSpan (Annotation.getLocA lDecl)
Syntax.InstD _ (Syntax.DataFamInstD _ dataFamInst) ->
let eqn = Syntax.dfid_eqn dataFamInst
familyName = Internal.extractIdPName $ Syntax.feqn_tycon eqn
in foldMap (\loc -> [(loc, familyName)]) $
Internal.locationFromSrcSpan (Annotation.getLocA lDecl)
_ -> []
-- | Associate family instance items with their family declarations.
associateFamilyInstanceParents ::
Map.Map Location.Location ItemName.ItemName ->
[Located.Located Item.Item] ->
[Located.Located Item.Item]
associateFamilyInstanceParents familyInstanceNames items =
let familyNameToKey = buildFamilyNameToKeyMap items
in fmap (resolveFamilyInstanceParent familyInstanceNames familyNameToKey) items
-- | Build a map from family names to their keys.
buildFamilyNameToKeyMap ::
[Located.Located Item.Item] ->
Map.Map ItemName.ItemName ItemKey.ItemKey
buildFamilyNameToKeyMap =
Map.fromList . concatMap getFamilyNameAndKey
where
getFamilyNameAndKey locItem =
let val = Located.value locItem
in case Item.name val of
Nothing -> []
Just name ->
if isFamilyKind (Item.kind val)
then [(name, Item.key val)]
else []
-- | Check if an item kind represents a family declaration.
isFamilyKind :: ItemKind.ItemKind -> Bool
isFamilyKind k = case k of
ItemKind.OpenTypeFamily -> True
ItemKind.DataFamily -> True
_ -> False
-- | Set the parentKey on a family instance item by looking up the family name.
resolveFamilyInstanceParent ::
Map.Map Location.Location ItemName.ItemName ->
Map.Map ItemName.ItemName ItemKey.ItemKey ->
Located.Located Item.Item ->
Located.Located Item.Item
resolveFamilyInstanceParent familyInstanceNames familyNameToKey locItem =
case Map.lookup (Located.location locItem) familyInstanceNames of
Nothing -> locItem
Just familyName ->
case Map.lookup familyName familyNameToKey of
Nothing -> locItem
Just parentKey ->
Internal.setParentKey parentKey locItem