{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Plugin.GhcTags.Generate
( GhcTag (..)
, GhcTags
, TagKind (..)
, tagKindToChar
, charToTagKind
, getGhcTags
) where
import Data.Maybe (mapMaybe)
import Data.Foldable (foldl')
import FastString ( FastString (..)
)
import HsBinds ( HsBindLR (..)
, PatSynBind (..)
, Sig (..)
)
import HsDecls ( ClsInstDecl (..)
, ConDecl (..)
, DataFamInstDecl (..)
, FamEqn (..)
, FamilyDecl (..)
, FamilyInfo (..)
, ForeignDecl (..)
, LHsDecl
, HsConDeclDetails
, HsDecl (..)
, HsDataDefn (..)
, InstDecl (..)
, TyClDecl (..)
, TyFamInstDecl (..)
)
import HsSyn ( FieldOcc (..)
, GhcPs
, HsModule (..)
, LFieldOcc
)
import HsTypes ( ConDeclField (..)
, HsConDetails (..)
, HsImplicitBndrs (..)
, HsType (..)
, LConDeclField
, LHsType
)
import SrcLoc ( GenLocated (..)
, Located
, SrcSpan (..)
, unLoc
)
import RdrName ( RdrName (..)
)
import Name ( nameOccName
, occNameFS
)
data TagKind = TkTerm
| TkFunction
| TkTypeConstructor
| TkDataConstructor
| TkGADTConstructor
| TkRecordField
| TkTypeSynonym
| TkTypeSignature
| TkPatternSynonym
| TkTypeClass
| TkTypeClassMember
| TkTypeClassInstance
| TkTypeFamily
| TkTypeFamilyInstance
| TkDataTypeFamily
| TkDataTypeFamilyInstance
| TkForeignImport
| TkForeignExport
deriving (Ord, Eq, Show)
tagKindToChar :: TagKind -> Char
tagKindToChar tagKind = case tagKind of
TkTerm -> 'x'
TkFunction -> 'l'
TkTypeConstructor -> 't'
TkDataConstructor -> 'c'
TkGADTConstructor -> 'g'
TkRecordField -> 'r'
TkTypeSynonym -> 'S'
TkTypeSignature -> 's'
TkPatternSynonym -> 'p'
TkTypeClass -> 'C'
TkTypeClassMember -> 'm'
TkTypeClassInstance -> 'i'
TkTypeFamily -> 'f'
TkTypeFamilyInstance -> 'F'
TkDataTypeFamily -> 'd'
TkDataTypeFamilyInstance -> 'D'
TkForeignImport -> 'I'
TkForeignExport -> 'E'
charToTagKind :: Char -> Maybe TagKind
charToTagKind c = case c of
'x' -> Just TkTerm
'l' -> Just TkFunction
't' -> Just TkTypeConstructor
'c' -> Just TkDataConstructor
'g' -> Just TkGADTConstructor
'r' -> Just TkRecordField
'S' -> Just TkTypeSynonym
's' -> Just TkTypeSignature
'p' -> Just TkPatternSynonym
'C' -> Just TkTypeClass
'm' -> Just TkTypeClassMember
'i' -> Just TkTypeClassInstance
'f' -> Just TkTypeFamily
'F' -> Just TkTypeFamilyInstance
'd' -> Just TkDataTypeFamily
'D' -> Just TkDataTypeFamilyInstance
'I' -> Just TkForeignImport
'E' -> Just TkForeignExport
_ -> Nothing
data GhcTag = GhcTag {
gtSrcSpan :: !SrcSpan
, gtTag :: !FastString
, gtKind :: !TagKind
}
deriving Show
type GhcTags = [GhcTag]
mkGhcTag :: Located RdrName
-> TagKind
-> GhcTag
mkGhcTag (L gtSrcSpan rdrName) gtKind =
case rdrName of
Unqual occName ->
GhcTag { gtTag = occNameFS occName
, gtSrcSpan
, gtKind
}
Qual _ occName ->
GhcTag { gtTag = occNameFS occName
, gtSrcSpan
, gtKind
}
Orig _ occName ->
GhcTag { gtTag = occNameFS occName
, gtSrcSpan
, gtKind
}
Exact name ->
GhcTag { gtTag = occNameFS $ nameOccName name
, gtSrcSpan
, gtKind
}
getGhcTags :: Located (HsModule GhcPs)
-> GhcTags
getGhcTags (L _ HsModule { hsmodDecls }) =
reverse $ foldl' go [] hsmodDecls
where
go :: GhcTags -> LHsDecl GhcPs -> GhcTags
go tags (L _ hsDecl) = case hsDecl of
TyClD _ tyClDecl ->
case tyClDecl of
FamDecl { tcdFam } ->
case mkFamilyDeclTags tcdFam of
Just tag -> tag : tags
Nothing -> tags
SynDecl { tcdLName } ->
mkGhcTag tcdLName TkTypeSynonym : tags
DataDecl { tcdLName, tcdDataDefn } ->
case tcdDataDefn of
HsDataDefn { dd_cons } ->
mkGhcTag tcdLName TkTypeConstructor
: (mkConsTags . unLoc) `concatMap` dd_cons
++ tags
XHsDataDefn {} -> tags
ClassDecl { tcdLName, tcdSigs, tcdMeths, tcdATs } ->
mkGhcTag tcdLName TkTypeClass
: (mkSigTags . unLoc) `concatMap` tcdSigs
++ foldl' (\tags' hsBind -> mkHsBindLRTags (unLoc hsBind) ++ tags')
tags
tcdMeths
++ (mkFamilyDeclTags . unLoc) `mapMaybe` tcdATs
XTyClDecl {} -> tags
InstD _ instDecl ->
case instDecl of
ClsInstD { cid_inst } ->
case cid_inst of
XClsInstDecl {} -> tags
ClsInstDecl { cid_poly_ty, cid_tyfam_insts, cid_datafam_insts } ->
case cid_poly_ty of
XHsImplicitBndrs {} ->
tyFamTags ++ dataFamTags ++ tags
HsIB { hsib_body } ->
case mkLHsTypeTag hsib_body of
Nothing -> tyFamTags ++ dataFamTags ++ tags
Just tag -> tag : tyFamTags ++ dataFamTags ++ tags
where
dataFamTags = (mkDataFamInstDeclTag . unLoc) `concatMap` cid_datafam_insts
tyFamTags = (mkTyFamInstDeclTag . unLoc) `mapMaybe` cid_tyfam_insts
DataFamInstD { dfid_inst } ->
mkDataFamInstDeclTag dfid_inst ++ tags
TyFamInstD { tfid_inst } ->
case mkTyFamInstDeclTag tfid_inst of
Nothing -> tags
Just tag -> tag : tags
XInstDecl {} -> tags
DerivD {} -> tags
ValD _ hsBind -> mkHsBindLRTags hsBind ++ tags
SigD _ sig -> mkSigTags sig ++ tags
DefD {} -> tags
ForD _ foreignDecl ->
case foreignDecl of
ForeignImport { fd_name } -> mkGhcTag fd_name TkForeignImport : tags
ForeignExport { fd_name } -> mkGhcTag fd_name TkForeignExport : tags
XForeignDecl {} -> tags
WarningD {} -> tags
AnnD {} -> tags
RuleD {} -> tags
SpliceD {} -> tags
DocD {} -> tags
RoleAnnotD {} -> tags
XHsDecl {} -> tags
mkConsTags :: ConDecl GhcPs -> GhcTags
mkConsTags ConDeclGADT { con_names, con_args } =
flip mkGhcTag TkGADTConstructor `map` con_names
++ mkHsConDeclDetails con_args
mkConsTags ConDeclH98 { con_name, con_args } =
mkGhcTag con_name TkDataConstructor
: mkHsConDeclDetails con_args
mkConsTags XConDecl {} = []
mkHsConDeclDetails :: HsConDeclDetails GhcPs -> GhcTags
mkHsConDeclDetails (RecCon (L _ fields)) = foldl' f [] fields
where
f :: GhcTags -> LConDeclField GhcPs -> GhcTags
f ts (L _ ConDeclField { cd_fld_names }) = foldl' g ts cd_fld_names
f ts _ = ts
g :: GhcTags -> LFieldOcc GhcPs -> GhcTags
g ts (L _ FieldOcc { rdrNameFieldOcc }) = mkGhcTag rdrNameFieldOcc TkRecordField : ts
g ts _ = ts
mkHsConDeclDetails _ = []
mkHsBindLRTags :: HsBindLR GhcPs GhcPs -> GhcTags
mkHsBindLRTags hsBind =
case hsBind of
FunBind { fun_id } -> [mkGhcTag fun_id TkFunction]
PatBind {} -> []
VarBind { var_id, var_rhs = L srcSpan _ } -> [mkGhcTag (L srcSpan var_id) TkTerm]
AbsBinds {} -> []
PatSynBind _ PSB { psb_id } -> [mkGhcTag psb_id TkPatternSynonym]
PatSynBind _ XPatSynBind {} -> []
XHsBindsLR {} -> []
mkSigTags :: Sig GhcPs -> GhcTags
mkSigTags (TypeSig _ lhs _) = flip mkGhcTag TkTypeSignature `map` lhs
mkSigTags (PatSynSig _ lhs _) = flip mkGhcTag TkPatternSynonym `map` lhs
mkSigTags (ClassOpSig _ _ lhs _) = flip mkGhcTag TkTypeClassMember `map` lhs
mkSigTags IdSig {} = []
mkSigTags FixSig {} = []
mkSigTags InlineSig {} = []
mkSigTags SpecSig {} = []
mkSigTags SpecInstSig {} = []
mkSigTags MinimalSig {} = []
mkSigTags SCCFunSig {} = []
mkSigTags CompleteMatchSig {} = []
mkSigTags XSig {} = []
mkFamilyDeclTags :: FamilyDecl GhcPs
-> Maybe GhcTag
mkFamilyDeclTags FamilyDecl { fdLName, fdInfo } = Just $ mkGhcTag fdLName tk
where
tk = case fdInfo of
DataFamily -> TkDataTypeFamily
OpenTypeFamily -> TkTypeFamily
ClosedTypeFamily {} -> TkTypeFamily
mkFamilyDeclTags XFamilyDecl {} = Nothing
mkLHsTypeTag :: LHsType GhcPs -> Maybe GhcTag
mkLHsTypeTag (L _ hsType) =
case hsType of
HsForAllTy {hst_body} -> mkLHsTypeTag hst_body
HsQualTy {hst_body} -> mkLHsTypeTag hst_body
HsTyVar _ _ a -> Just $ mkGhcTag a TkTypeClassInstance
HsAppTy _ a _ -> mkLHsTypeTag a
HsOpTy _ _ a _ -> Just $ mkGhcTag a TkTypeClassInstance
HsKindSig _ a _ -> mkLHsTypeTag a
_ -> Nothing
mkDataFamInstDeclTag :: DataFamInstDecl GhcPs -> GhcTags
mkDataFamInstDeclTag DataFamInstDecl { dfid_eqn } =
case dfid_eqn of
XHsImplicitBndrs {} -> []
HsIB { hsib_body = FamEqn { feqn_tycon, feqn_rhs } } ->
case feqn_rhs of
HsDataDefn { dd_cons } ->
mkGhcTag feqn_tycon TkDataTypeFamilyInstance : (mkConsTags . unLoc) `concatMap` dd_cons
XHsDataDefn {} ->
mkGhcTag feqn_tycon TkDataTypeFamilyInstance : []
HsIB { hsib_body = XFamEqn {} } -> []
mkTyFamInstDeclTag :: TyFamInstDecl GhcPs -> Maybe GhcTag
mkTyFamInstDeclTag TyFamInstDecl { tfid_eqn } =
case tfid_eqn of
XHsImplicitBndrs {} -> Nothing
HsIB { hsib_body = FamEqn { feqn_tycon } } -> Just $ mkGhcTag feqn_tycon TkTypeFamilyInstance
HsIB { hsib_body = XFamEqn {} } -> Nothing