packages feed

scrod-0.2026.2.21: source/library/Scrod/Convert/FromGhc/ParentAssociation.hs

-- | Shared logic for associating pragma items with their target declarations.
--
-- Multiple pragma types (fixity, inline, specialise, warning, role) follow
-- the same pattern: items at known pragma locations are parented to the
-- declaration with the matching name. This module provides a generic
-- implementation of that pattern.
module Scrod.Convert.FromGhc.ParentAssociation where

import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import qualified Data.Set as Set
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.ItemName as ItemName
import qualified Scrod.Core.Located as Located
import qualified Scrod.Core.Location as Location

-- | Associate pragma items with their target declarations by name.
--
-- The first set contains all pragma locations (used to exclude pragmas
-- from the name-to-key map). The second set contains only the locations
-- for the specific pragma type being resolved.
associateParents ::
  Set.Set Location.Location ->
  Set.Set Location.Location ->
  [Located.Located Item.Item] ->
  [Located.Located Item.Item]
associateParents allPragmaLocations pragmaLocations items =
  let nameToKey = buildNameToKeyMap allPragmaLocations items
   in fmap (resolveParent pragmaLocations nameToKey) items

-- | Build a map from item names to their keys, excluding pragma items
-- and child items. Only top-level declarations (those with no parentKey)
-- are eligible parents, so that @data T = T@ maps to the type
-- declaration rather than the constructor.
buildNameToKeyMap ::
  Set.Set Location.Location ->
  [Located.Located Item.Item] ->
  Map.Map ItemName.ItemName ItemKey.ItemKey
buildNameToKeyMap allPragmaLocations =
  Map.fromList . concatMap getNameAndKey
  where
    getNameAndKey locItem =
      let val = Located.value locItem
       in case Item.name val of
            Nothing -> []
            Just name ->
              if Set.member (Located.location locItem) allPragmaLocations
                || Maybe.isJust (Item.parentKey val)
                then []
                else [(name, Item.key val)]

-- | Set the parentKey on a pragma item by looking up its name.
resolveParent ::
  Set.Set Location.Location ->
  Map.Map ItemName.ItemName ItemKey.ItemKey ->
  Located.Located Item.Item ->
  Located.Located Item.Item
resolveParent pragmaLocations nameToKey locItem =
  if Set.member (Located.location locItem) pragmaLocations
    then case Item.name (Located.value locItem) of
      Nothing -> locItem
      Just name ->
        case Map.lookup name nameToKey of
          Nothing -> locItem
          Just parentKey ->
            Internal.setParentKey parentKey locItem
    else locItem