scrod-0.2026.2.21: source/library/Scrod/Convert/FromGhc/SigArguments.hs
-- | Argument and return-type extraction from GHC type signatures.
--
-- Provides functions to walk the 'HsFunTy' chain of a type signature,
-- collecting each argument's pretty-printed type text and its optional
-- Haddock documentation. Also provides utilities to strip 'HsDocTy'
-- wrappers from types before pretty-printing.
module Scrod.Convert.FromGhc.SigArguments where
import qualified Data.Text as Text
import GHC.Hs ()
import qualified GHC.Hs.Doc as HsDoc
import qualified GHC.Hs.Extension as Ghc
import qualified GHC.Types.SrcLoc as SrcLoc
import qualified GHC.Utils.Outputable as Outputable
import qualified Language.Haskell.Syntax as Syntax
import qualified Scrod.Convert.FromGhc.Internal as Internal
-- | Extract argument types and their optional doc comments from a type
-- signature. Walks the 'HsFunTy' chain, collecting each argument's
-- pretty-printed type text and its 'LHsDoc' (if the argument was wrapped
-- in 'HsDocTy'). The return type is included only when it has documentation.
--
-- Returns a pair of (arguments, optional return type).
--
-- Handles 'TypeSig' (unwrap via 'hswc_body'), 'PatSynSig', and
-- 'ClassOpSig' (unwrap via 'sig_body' on 'HsSigType').
extractSigArguments ::
Syntax.Sig Ghc.GhcPs ->
( [(Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))],
Maybe (Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))
)
extractSigArguments sig = case sig of
Syntax.TypeSig _ _ wc ->
extractArgsFromBody . Syntax.sig_body . SrcLoc.unLoc $ Syntax.hswc_body wc
Syntax.PatSynSig _ _ lSigType ->
extractArgsFromBody . Syntax.sig_body $ SrcLoc.unLoc lSigType
Syntax.ClassOpSig _ _ _ lSigType ->
extractArgsFromBody . Syntax.sig_body $ SrcLoc.unLoc lSigType
_ -> ([], Nothing)
-- | Skip through 'HsForAllTy' and 'HsQualTy' to reach the arrow chain,
-- then extract arguments from the 'HsFunTy' chain. Returns the arguments
-- and the optional documented return type separately.
extractArgsFromBody ::
Syntax.LHsType Ghc.GhcPs ->
( [(Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))],
Maybe (Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))
)
extractArgsFromBody lTy = case SrcLoc.unLoc lTy of
Syntax.HsForAllTy _ _ body -> extractArgsFromBody body
Syntax.HsQualTy _ _ body -> extractArgsFromBody body
Syntax.HsFunTy _ _ arg res ->
let (args, ret) = extractArgsFromBody res
in (extractArg arg : args, ret)
Syntax.HsDocTy _ inner _doc -> case SrcLoc.unLoc inner of
Syntax.HsFunTy _ _ arg res ->
let (args, ret) = extractArgsFromBody res
in (extractArg arg : args, ret)
_ -> ([], Just (extractArg lTy))
_ -> ([], Nothing)
-- | Extract the type text and optional doc comment from a single argument.
-- If the argument is wrapped in 'HsDocTy', the doc is extracted and the
-- inner type is pretty-printed (with any nested doc annotations stripped).
-- Otherwise, the argument is pretty-printed as-is with no doc.
extractArg ::
Syntax.LHsType Ghc.GhcPs -> (Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))
extractArg lTy = case SrcLoc.unLoc lTy of
Syntax.HsDocTy _ inner doc ->
( Text.pack . Internal.showSDocShort . Outputable.ppr $ stripHsDocTy inner,
Just doc
)
_ ->
( Text.pack . Internal.showSDocShort . Outputable.ppr $ stripHsDocTy lTy,
Nothing
)
-- | Strip 'HsDocTy' wrappers from a wildcard-wrapped signature type.
stripHsSigWcType ::
Syntax.LHsSigWcType Ghc.GhcPs -> Syntax.LHsSigWcType Ghc.GhcPs
stripHsSigWcType wc =
wc {Syntax.hswc_body = stripHsSigType (Syntax.hswc_body wc)}
-- | Strip 'HsDocTy' wrappers from a signature type.
stripHsSigType :: Syntax.LHsSigType Ghc.GhcPs -> Syntax.LHsSigType Ghc.GhcPs
stripHsSigType lSigType = case SrcLoc.unLoc lSigType of
Syntax.HsSig {Syntax.sig_ext = ext, Syntax.sig_bndrs = bndrs, Syntax.sig_body = body} ->
let stripped = stripHsDocTy body
in SrcLoc.L
(SrcLoc.getLoc lSigType)
Syntax.HsSig
{ Syntax.sig_ext = ext,
Syntax.sig_bndrs = bndrs,
Syntax.sig_body = stripped
}
-- | Recursively strip 'HsDocTy' wrappers from a located type.
-- Recurses into 'HsFunTy', 'HsForAllTy', 'HsQualTy', and 'HsParTy' to
-- ensure all embedded doc comments are removed.
stripHsDocTy :: Syntax.LHsType Ghc.GhcPs -> Syntax.LHsType Ghc.GhcPs
stripHsDocTy lTy = case lTy of
SrcLoc.L ann hsType -> case hsType of
Syntax.HsDocTy _ inner _ -> stripHsDocTy inner
Syntax.HsFunTy x mult arg res ->
SrcLoc.L ann $ Syntax.HsFunTy x mult (stripHsDocTy arg) (stripHsDocTy res)
Syntax.HsForAllTy x tele body ->
SrcLoc.L ann $ Syntax.HsForAllTy x tele (stripHsDocTy body)
Syntax.HsQualTy x ctx body ->
SrcLoc.L ann $ Syntax.HsQualTy x ctx (stripHsDocTy body)
Syntax.HsParTy x inner ->
SrcLoc.L ann $ Syntax.HsParTy x (stripHsDocTy inner)
_ -> lTy