aboutsummaryrefslogtreecommitdiff
path: root/haddock-api/src/Haddock/GhcUtils.hs
diff options
context:
space:
mode:
Diffstat (limited to 'haddock-api/src/Haddock/GhcUtils.hs')
-rw-r--r--haddock-api/src/Haddock/GhcUtils.hs274
1 files changed, 250 insertions, 24 deletions
diff --git a/haddock-api/src/Haddock/GhcUtils.hs b/haddock-api/src/Haddock/GhcUtils.hs
index e7d80969..5cc005cc 100644
--- a/haddock-api/src/Haddock/GhcUtils.hs
+++ b/haddock-api/src/Haddock/GhcUtils.hs
@@ -1,5 +1,6 @@
-{-# LANGUAGE BangPatterns, FlexibleInstances, ViewPatterns #-}
+{-# LANGUAGE BangPatterns, StandaloneDeriving, FlexibleInstances, ViewPatterns #-}
{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_HADDOCK hide #-}
-----------------------------------------------------------------------------
@@ -18,20 +19,34 @@ module Haddock.GhcUtils where
import Control.Arrow
+import Data.Char ( isSpace )
+
import Haddock.Types( DocNameI )
import Exception
-import Outputable
+import FV
+import Outputable ( Outputable, panic, showPpr )
import Name
import NameSet
-import Lexeme
import Module
import HscTypes
import GHC
import Class
import DynFlags
+import SrcLoc ( advanceSrcLoc )
+import Var ( VarBndr(..), TyVarBinder, tyVarKind, updateTyVarKind,
+ isInvisibleArgFlag )
+import VarSet ( VarSet, emptyVarSet )
+import VarEnv ( TyVarEnv, extendVarEnv, elemVarEnv, emptyVarEnv )
+import TyCoRep ( Type(..), isRuntimeRepVar )
+import TysWiredIn( liftedRepDataConTyCon )
+
+import StringBuffer ( StringBuffer )
+import qualified StringBuffer as S
-import HsTypes (HsType(..))
+import Data.ByteString ( ByteString )
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Internal as BS
moduleString :: Module -> String
@@ -40,15 +55,8 @@ moduleString = moduleNameString . moduleName
isNameSym :: Name -> Bool
isNameSym = isSymOcc . nameOccName
-
-isVarSym :: OccName -> Bool
-isVarSym = isLexVarSym . occNameFS
-
-isConSym :: OccName -> Bool
-isConSym = isLexConSym . occNameFS
-
-
-getMainDeclBinder :: HsDecl name -> [IdP name]
+getMainDeclBinder :: (SrcSpanLess (LPat p) ~ Pat p , HasSrcSpan (LPat p)) =>
+ HsDecl p -> [IdP p]
getMainDeclBinder (TyClD _ d) = [tcdName d]
getMainDeclBinder (ValD _ d) =
case collectHsBindBinders d of
@@ -141,12 +149,6 @@ isValD :: HsDecl a -> Bool
isValD (ValD _ _) = True
isValD _ = False
-
-declATs :: HsDecl a -> [IdP a]
-declATs (TyClD _ d) | isClassDecl d = map (unL . fdLName . unL) $ tcdATs d
-declATs _ = []
-
-
pretty :: Outputable a => DynFlags -> a -> String
pretty = showPpr
@@ -237,6 +239,8 @@ getGADTConTypeG (XConDecl {}) = panic "getGADTConTypeG"
data Precedence
= PREC_TOP -- ^ precedence of 'type' production in GHC's parser
+ | PREC_SIG -- ^ explicit type signature
+
| PREC_CTX -- ^ Used for single contexts, eg. ctx => type
-- (as opposed to (ctx1, ctx2) => type)
@@ -263,22 +267,27 @@ reparenTypePrec = go
go _ (HsBangTy x b ty) = HsBangTy x b (reparenLType ty)
go _ (HsTupleTy x con tys) = HsTupleTy x con (map reparenLType tys)
go _ (HsSumTy x tys) = HsSumTy x (map reparenLType tys)
- go _ (HsKindSig x ty kind) = HsKindSig x (reparenLType ty) (reparenLType kind)
go _ (HsListTy x ty) = HsListTy x (reparenLType ty)
go _ (HsRecTy x flds) = HsRecTy x (map (fmap reparenConDeclField) flds)
go p (HsDocTy x ty d) = HsDocTy x (goL p ty) d
go _ (HsExplicitListTy x p tys) = HsExplicitListTy x p (map reparenLType tys)
go _ (HsExplicitTupleTy x tys) = HsExplicitTupleTy x (map reparenLType tys)
+ go p (HsKindSig x ty kind)
+ = paren p PREC_SIG $ HsKindSig x (goL PREC_SIG ty) (goL PREC_SIG kind)
go p (HsIParamTy x n ty)
- = paren p PREC_CTX $ HsIParamTy x n (reparenLType ty)
+ = paren p PREC_SIG $ HsIParamTy x n (reparenLType ty)
go p (HsForAllTy x tvs ty)
= paren p PREC_CTX $ HsForAllTy x (map (fmap reparenTyVar) tvs) (reparenLType ty)
go p (HsQualTy x ctxt ty)
- = paren p PREC_FUN $ HsQualTy x (fmap (map reparenLType) ctxt) (reparenLType ty)
+ = let p' [_] = PREC_CTX
+ p' _ = PREC_TOP -- parens will get added anyways later...
+ in paren p PREC_CTX $ HsQualTy x (fmap (\xs -> map (goL (p' xs)) xs) ctxt) (goL PREC_TOP ty)
go p (HsFunTy x ty1 ty2)
= paren p PREC_FUN $ HsFunTy x (goL PREC_FUN ty1) (goL PREC_TOP ty2)
go p (HsAppTy x fun_ty arg_ty)
= paren p PREC_CON $ HsAppTy x (goL PREC_FUN fun_ty) (goL PREC_CON arg_ty)
+ go p (HsAppKindTy x fun_ty arg_ki)
+ = paren p PREC_CON $ HsAppKindTy x (goL PREC_FUN fun_ty) (goL PREC_CON arg_ki)
go p (HsOpTy x ty1 op ty2)
= paren p PREC_FUN $ HsOpTy x (goL PREC_OP ty1) op (goL PREC_OP ty2)
go p (HsParTy _ t) = unLoc $ goL p t -- pretend the paren doesn't exist - it will be added back if needed
@@ -426,13 +435,230 @@ minimalDef n = do
-------------------------------------------------------------------------------
-setObjectDir, setHiDir, setStubDir, setOutputDir :: String -> DynFlags -> DynFlags
+setObjectDir, setHiDir, setHieDir, setStubDir, setOutputDir :: String -> DynFlags -> DynFlags
setObjectDir f d = d{ objectDir = Just f}
setHiDir f d = d{ hiDir = Just f}
+setHieDir f d = d{ hieDir = Just f}
setStubDir f d = d{ stubDir = Just f
, includePaths = addGlobalInclude (includePaths d) [f] }
-- -stubdir D adds an implicit -I D, so that gcc can find the _stub.h file
-- \#included from the .hc file when compiling with -fvia-C.
-setOutputDir f = setObjectDir f . setHiDir f . setStubDir f
+setOutputDir f = setObjectDir f . setHiDir f . setHieDir f . setStubDir f
+
+
+-------------------------------------------------------------------------------
+-- * 'StringBuffer' and 'ByteString'
+-------------------------------------------------------------------------------
+-- We get away with a bunch of these functions because 'StringBuffer' and
+-- 'ByteString' have almost exactly the same structure.
+
+-- | Convert a UTF-8 encoded 'ByteString' into a 'StringBuffer. This really
+-- relies on the internals of both 'ByteString' and 'StringBuffer'.
+--
+-- /O(n)/ (but optimized into a @memcpy@ by @bytestring@ under the hood)
+stringBufferFromByteString :: ByteString -> StringBuffer
+stringBufferFromByteString bs =
+ let BS.PS fp off len = bs <> BS.pack [0,0,0]
+ in S.StringBuffer { S.buf = fp, S.len = len - 3, S.cur = off }
+
+-- | Take the first @n@ /bytes/ of the 'StringBuffer' and put them in a
+-- 'ByteString'.
+--
+-- /O(1)/
+takeStringBuffer :: Int -> StringBuffer -> ByteString
+takeStringBuffer !n !(S.StringBuffer fp _ cur) = BS.PS fp cur n
+
+-- | Return the prefix of the first 'StringBuffer' that /isn't/ in the second
+-- 'StringBuffer'. **The behavior is undefined if the 'StringBuffers' use
+-- separate buffers.**
+--
+-- /O(1)/
+splitStringBuffer :: StringBuffer -> StringBuffer -> ByteString
+splitStringBuffer buf1 buf2 = takeStringBuffer n buf1
+ where n = S.byteDiff buf1 buf2
+
+-- | Split the 'StringBuffer' at the next newline (or the end of the buffer).
+-- Also: initial position is passed in and the updated position is returned.
+--
+-- /O(n)/ (but /O(1)/ space)
+spanLine :: RealSrcLoc -> StringBuffer -> (ByteString, RealSrcLoc, StringBuffer)
+spanLine !loc !buf = go loc buf
+ where
+
+ go !l !b
+ | not (S.atEnd b)
+ = case S.nextChar b of
+ ('\n', b') -> (splitStringBuffer buf b', advanceSrcLoc l '\n', b')
+ (c, b') -> go (advanceSrcLoc l c) b'
+ | otherwise
+ = (splitStringBuffer buf b, advanceSrcLoc l '\n', b)
+-- | Given a start position and a buffer with that start position, split the
+-- buffer at an end position.
+--
+-- /O(n)/ (but /O(1)/ space)
+spanPosition :: RealSrcLoc -- ^ start of buffeer
+ -> RealSrcLoc -- ^ position until which to take
+ -> StringBuffer -- ^ buffer from which to take
+ -> (ByteString, StringBuffer)
+spanPosition !start !end !buf = go start buf
+ where
+
+ go !l !b
+ | l < end
+ , not (S.atEnd b)
+ , (c, b') <- S.nextChar b
+ = go (advanceSrcLoc l c) b'
+ | otherwise
+ = (splitStringBuffer buf b, b)
+
+-- | Try to parse a line of CPP from the from of the buffer. A \"line\" of CPP
+-- consists of
+--
+-- * at most 10 whitespace characters, including at least one newline
+-- * a @#@ character
+-- * keep parsing lines until you find a line not ending in @\\@.
+--
+-- This is chock full of heuristics about what a line of CPP is.
+--
+-- /O(n)/ (but /O(1)/ space)
+tryCppLine :: RealSrcLoc -> StringBuffer -> Maybe (ByteString, RealSrcLoc, StringBuffer)
+tryCppLine !loc !buf = spanSpace (S.prevChar buf '\n' == '\n') loc buf
+ where
+
+ -- Keep consuming space characters until we hit either a @#@ or something
+ -- else. If we hit a @#@, start parsing CPP
+ spanSpace !seenNl !l !b
+ | S.atEnd b
+ = Nothing
+ | otherwise
+ = case S.nextChar b of
+ ('#' , b') | not (S.atEnd b')
+ , ('-', b'') <- S.nextChar b'
+ , ('}', _) <- S.nextChar b''
+ -> Nothing -- Edge case exception for @#-}@
+ | seenNl
+ -> Just (spanCppLine (advanceSrcLoc l '#') b') -- parse CPP
+ | otherwise
+ -> Nothing -- We didn't see a newline, so this can't be CPP!
+
+ (c , b') | isSpace c -> spanSpace (seenNl || c == '\n')
+ (advanceSrcLoc l c) b'
+ | otherwise -> Nothing
+
+ -- Consume a CPP line to its "end" (basically the first line that ends not
+ -- with a @\@ character)
+ spanCppLine !l !b
+ | S.atEnd b
+ = (splitStringBuffer buf b, l, b)
+ | otherwise
+ = case S.nextChar b of
+ ('\\', b') | not (S.atEnd b')
+ , ('\n', b'') <- S.nextChar b'
+ -> spanCppLine (advanceSrcLoc (advanceSrcLoc l '\\') '\n') b''
+
+ ('\n', b') -> (splitStringBuffer buf b', advanceSrcLoc l '\n', b')
+
+ (c , b') -> spanCppLine (advanceSrcLoc l c) b'
+
+-------------------------------------------------------------------------------
+-- * Free variables of a 'Type'
+-------------------------------------------------------------------------------
+
+-- | Get free type variables in a 'Type' in their order of appearance.
+-- See [Ordering of implicit variables].
+orderedFVs
+ :: VarSet -- ^ free variables to ignore
+ -> [Type] -- ^ types to traverse (in order) looking for free variables
+ -> [TyVar] -- ^ free type variables, in the order they appear in
+orderedFVs vs tys =
+ reverse . fst $ tyCoFVsOfTypes' tys (const True) vs ([], emptyVarSet)
+
+
+-- See the "Free variables of types and coercions" section in 'TyCoRep', or
+-- check out Note [Free variables of types]. The functions in this section
+-- don't output type variables in the order they first appear in in the 'Type'.
+--
+-- For example, 'tyCoVarsOfTypeList' reports an incorrect order for the type
+-- of 'const :: a -> b -> a':
+--
+-- >>> import Name
+-- >>> import TyCoRep
+-- >>> import TysPrim
+-- >>> import Var
+-- >>> a = TyVarTy alphaTyVar
+-- >>> b = TyVarTy betaTyVar
+-- >>> constTy = mkFunTys [a, b] a
+-- >>> map (getOccString . tyVarName) (tyCoVarsOfTypeList constTy)
+-- ["b","a"]
+--
+-- However, we want to reuse the very optimized traversal machinery there, so
+-- so we make our own `tyCoFVsOfType'`, `tyCoFVsBndr'`, and `tyCoVarsOfTypes'`.
+-- All these do differently is traverse in a different order and ignore
+-- coercion variables.
+
+-- | Just like 'tyCoFVsOfType', but traverses type variables in reverse order
+-- of appearance.
+tyCoFVsOfType' :: Type -> FV
+tyCoFVsOfType' (TyVarTy v) a b c = (FV.unitFV v `unionFV` tyCoFVsOfType' (tyVarKind v)) a b c
+tyCoFVsOfType' (TyConApp _ tys) a b c = tyCoFVsOfTypes' tys a b c
+tyCoFVsOfType' (LitTy {}) a b c = emptyFV a b c
+tyCoFVsOfType' (AppTy fun arg) a b c = (tyCoFVsOfType' arg `unionFV` tyCoFVsOfType' fun) a b c
+tyCoFVsOfType' (FunTy arg res) a b c = (tyCoFVsOfType' res `unionFV` tyCoFVsOfType' arg) a b c
+tyCoFVsOfType' (ForAllTy bndr ty) a b c = tyCoFVsBndr' bndr (tyCoFVsOfType' ty) a b c
+tyCoFVsOfType' (CastTy ty _) a b c = (tyCoFVsOfType' ty) a b c
+tyCoFVsOfType' (CoercionTy _ ) a b c = emptyFV a b c
+
+-- | Just like 'tyCoFVsOfTypes', but traverses type variables in reverse order
+-- of appearance.
+tyCoFVsOfTypes' :: [Type] -> FV
+tyCoFVsOfTypes' (ty:tys) fv_cand in_scope acc = (tyCoFVsOfTypes' tys `unionFV` tyCoFVsOfType' ty) fv_cand in_scope acc
+tyCoFVsOfTypes' [] fv_cand in_scope acc = emptyFV fv_cand in_scope acc
+
+-- | Just like 'tyCoFVsBndr', but traverses type variables in reverse order of
+-- appearance.
+tyCoFVsBndr' :: TyVarBinder -> FV -> FV
+tyCoFVsBndr' (Bndr tv _) fvs = FV.delFV tv fvs `unionFV` tyCoFVsOfType' (tyVarKind tv)
+
+
+-------------------------------------------------------------------------------
+-- * Defaulting RuntimeRep variables
+-------------------------------------------------------------------------------
+
+-- | Traverses the type, defaulting type variables of kind 'RuntimeRep' to
+-- 'LiftedType'. See 'defaultRuntimeRepVars' in IfaceType.hs the original such
+-- function working over `IfaceType`'s.
+defaultRuntimeRepVars :: Type -> Type
+defaultRuntimeRepVars = go emptyVarEnv
+ where
+ go :: TyVarEnv () -> Type -> Type
+ go subs (ForAllTy (Bndr var flg) ty)
+ | isRuntimeRepVar var
+ , isInvisibleArgFlag flg
+ = let subs' = extendVarEnv subs var ()
+ in go subs' ty
+ | otherwise
+ = ForAllTy (Bndr (updateTyVarKind (go subs) var) flg)
+ (go subs ty)
+
+ go subs (TyVarTy tv)
+ | tv `elemVarEnv` subs
+ = TyConApp liftedRepDataConTyCon []
+ | otherwise
+ = TyVarTy (updateTyVarKind (go subs) tv)
+
+ go subs (TyConApp tc tc_args)
+ = TyConApp tc (map (go subs) tc_args)
+
+ go subs (FunTy arg res)
+ = FunTy (go subs arg) (go subs res)
+
+ go subs (AppTy t u)
+ = AppTy (go subs t) (go subs u)
+
+ go subs (CastTy x co)
+ = CastTy (go subs x) co
+
+ go _ ty@(LitTy {}) = ty
+ go _ ty@(CoercionTy {}) = ty