packages feed

smuggler2-0.3.3.2: src/Smuggler2/Exports.hs

-- |
-- Description: Utility functions for generating an export list ast and
-- associated Anns.  It's a bit fiddlier than it could be because ghc's
-- functions for producing exportable things generates @AvailInfo@ from which we
-- need to reconstitue @IEWrappedName@ and then @IE@
module Smuggler2.Exports
  ( mkExportAnnT
  )
where

import Avail ( AvailInfo(..) )
import GHC
    ( AnnKeywordId(AnnCloseP, AnnVal, AnnType, AnnPattern, AnnDotdot,
                   AnnOpenP),
      GhcPs,
      IE(IEThingAbs, IEVar, IEThingAll),
      IEWrappedName(IEName, IEType, IEPattern),
      LIEWrappedName,
      RdrName )
import GhcPlugins ( Located, mkVarUnqual )
import Language.Haskell.GHC.ExactPrint ( TransformT )
import Language.Haskell.GHC.ExactPrint.Types
    ( noExt, DeltaPos(DP), KeywordId(G) )
import Lexeme ( isLexSym )
import Name
    ( Name,
      OccName(occNameFS),
      getOccString,
      isDataOcc,
      isSymOcc,
      isTcOcc,
      HasOccName(occName) )
import Smuggler2.Anns ( mkLocWithAnns, mkLoc )

-- | Generates the annotations for a name, wrapping () around symbollic names
mkLIEName ::
  Monad m =>
  Name ->
  TransformT m (LIEWrappedName RdrName)
mkLIEName name
  | isTcOcc occ && isSymOcc occ = do
    lname <-
      mkLocWithAnns
        (mkVarUnqual nameFS)
        (DP (0, 0)) -- for a gap afer @type@
        ann
    mkLocWithAnns (IEType lname) (DP (1, 2)) [(G AnnType, DP (0, 0))]
  | isDataOcc occ = do
    lname <-
      mkLocWithAnns
        (mkVarUnqual nameFS)
        (DP (0, 1)) -- for a gap after @pattern@
        ann
    mkLocWithAnns (IEPattern lname) (DP (1, 2)) [(G AnnPattern, DP (0, 0))]
  | otherwise = do
    lname <-
      mkLocWithAnns
        (mkVarUnqual nameFS)
        (DP (0, 0))
        ann
    mkLocWithAnns (IEName lname) (DP (1, 2)) []
  where
    occ = occName name
    nameFS = occNameFS occ
    ann =
      if isLexSym nameFS -- infix type or data constructor / identifier, so add ()
        then [(G AnnOpenP, DP (0, 0)), (G AnnVal, DP (0, 0)), (G AnnCloseP, DP (0, 0))]
        else [(G AnnVal, DP (0, 0))]

-- | Uses an exportable thing to generate the corresponding
-- piece of (annotated) AST.
mkExportAnnT :: (Monad m) => AvailInfo -> TransformT m (Located (IE GhcPs))
-- Ordinary identifier
mkExportAnnT (Avail name) = do
  liename <- mkLIEName name
  mkLoc (IEVar noExt liename)

-- A type or class.  Since we expect @name@ to be in scope, it should be the head
-- of @names@
mkExportAnnT (AvailTC name names fieldlabels) = do
  liename <- mkLIEName name

  -- Could export pieces explicitly, but this becomes ugly;
  -- operators need to be wrapped in (), etc, so just export things
  -- with pieces by wildcard
  let lienameWithWildcard =
        mkLocWithAnns
          (IEThingAll noExt liename)
          (DP (0, 0))
          [(G AnnOpenP, DP (0, 0)), (G AnnDotdot, DP (0, 0)), (G AnnCloseP, DP (0, 0))]

  case (names, fieldlabels) of
    -- This case implies that the type or class is not to be in scope
    -- which should not happen as we should only be processing exportable things
    -- Alternativey, could just: mkLoc (IEThingAbs noExt liename)
    ([], _) ->
      error $
        "smuggler: trying to export type class that is not to be in scope "
          ++ getOccString name
    -- A type class with no pieces
    ([_typeclass], []) -> mkLoc (IEThingAbs noExt liename)
    -- A type class with no pieces, but with field selectors.  A record type?
    ([_typeclass], _fl) -> lienameWithWildcard
    -- A type class with pieces
    (typeorclass : _pieces, _fl) ->
      if name == typeorclass -- check AvailTC invariant
        then lienameWithWildcard
        else
          error $
            "smuggler: broken AvailTC invariant: "
              ++ getOccString name
              ++ "/="
              ++ getOccString typeorclass